J'ai un script Shell produisant des données comme ceci:
1234567890 *
1234567891 *
Je dois supprimer JUSTE les trois derniers caractères "*". Je sais que je peux le faire via
(whatever) | sed 's/\(.*\).../\1/'
Mais je ne veux pas utiliser sed pour des raisons de vitesse. Ce seront toujours les mêmes 3 derniers caractères.
Un moyen rapide de nettoyer la sortie?
En supposant que toutes les données soient formatées comme dans votre exemple, utilisez ' cut ' pour obtenir la première colonne uniquement.
cat $file | cut -d ' ' -f 1
ou pour obtenir les 10 premiers caractères.
cat $file | cut -c 1-10
Voici un tour Unix à l'ancienne pour supprimer les 3 derniers caractères d'une ligne qui n'utilise pas sed OR awk ...
> echo 987654321 | rev | cut -c 4- | rev
987654
Contrairement à l'exemple précédent utilisant "cut", cela ne nécessite pas de connaître la longueur de la ligne.
Je peux vous garantir que bash
ne sera pas plus rapide que sed
pour cette tâche. Démarrer des processus externes dans bash
est généralement une mauvaise idée, mais seulement si vous le faites souvent.
Donc, si vous démarrez un processus sed
pour chaque ligne de votre entrée, je serais inquiet. Mais tu n'est pas. Il vous suffit de commencer un sed
qui fera tout le travail à votre place.
Vous pouvez cependant constater que le sed
suivant sera un peu plus rapide que votre version:
(whatever) | sed 's/...$//'
Cela ne fait que supprimer les trois derniers caractères de chaque ligne, plutôt que de remplacer la ligne entière par une version plus courte d'elle-même. Maintenant, peut-être que des moteurs RE plus modernes peuvent optimiser votre commande, mais pourquoi prendre le risque?.
Pour être honnête, la seule façon dont je pourrais penser que ce serait plus rapide serait de créer à la main votre propre programme de filtrage basé sur le langage C. Et la seule raison pour laquelle peut être plus rapide que sed
est que vous pouvez tirer parti des connaissances supplémentaires que vous avez sur vos besoins en matière de traitement ( sed
doit permettre une procession généralisée, donc peut-être plus lent à cause de cela).
N'oubliez pas le mantra de l'optimisation: "Mesurer, ne devinez pas!"
Si vous voulez vraiment faire ceci ligne par ligne dans bash
(et je maintiens toujours que c'est une mauvaise idée), vous peut utiliser:
pax> line=123456789abc
pax> line2=${line%%???}
pax> echo ${line2}
123456789
pax> _
Vous voudrez peut-être également vérifier si vous avez réellement besoin d’une amélioration de la vitesse. Si vous traitez les lignes comme un gros morceau, vous verrez que sed
est très rapide. Tapez ce qui suit:
#!/usr/bin/bash
echo This is a pretty chunky line with three bad characters at the end.XXX >qq1
for i in 4 16 64 256 1024 4096 16384 65536 ; do
cat qq1 qq1 >qq2
cat qq2 qq2 >qq1
done
head -20000l qq1 >qq2
wc -l qq2
date
time sed 's/...$//' qq2 >qq1
date
head -3l qq1
et l'exécuter. Voici la sortie sur mon ordinateur portable R40 (pas très rapide du tout):
pax> ./chk.sh
20000 qq2
Sat Jul 24 13:09:15 WAST 2010
real 0m0.851s
user 0m0.781s
sys 0m0.050s
Sat Jul 24 13:09:16 WAST 2010
This is a pretty chunky line with three bad characters at the end.
This is a pretty chunky line with three bad characters at the end.
This is a pretty chunky line with three bad characters at the end.
Cela représente 20 000 lignes en moins d'une seconde, ce qui est plutôt bien pour une tâche effectuée toutes les heures.
$ x="can_haz"
$ echo "${x%???}"
can_
awk
et sed
sont tous deux très rapides, mais si vous pensez que c'est important, n'hésitez pas à utiliser l'un des éléments suivants:
Si les caractères que vous souhaitez supprimer sont toujours à la fin de la chaîne
echo '1234567890 *' | tr -d ' *'
S'ils peuvent apparaître n'importe où dans la chaîne et que vous voulez seulement les supprimer à la fin
echo '1234567890 *' | rev | cut -c 4- | rev
Les pages de manuel de toutes les commandes expliqueront ce qui se passe.
Je pense que vous devriez utiliser sed
, cependant.
Tu pourrais essayer
(whatever) | while read line; do echo $line | head --bytes -3; done;
head
devrait lui-même être plus rapide que sed
ou cut
car il n'y a pas de correspondance de regex ou de delimètre, mais appeler une pour chaque ligne séparément l'emporterait probablement sur celui.
Note: Cette réponse est un peu une plaisanterie, mais ça marche vraiment ...
#!/bin/bash
outfile="/tmp/$RANDOM"
cfile="$outfile.c"
echo '#include <stdio.h>
int main(void){int e=1;char c;while((c=getc(stdin))!=-1){if(c==10)e=1;if(c==32)e=0;if(e)putc(c,stdout);}}' >> "$cfile"
gcc -o "$outfile" "$cfile"
rm "$cfile"
cat somedata.txt | "$outfile"
rm "$outfile"
Vous pouvez remplacer cat somedata.txt
avec une commande différente.
Si le script génère toujours des lignes de 10 caractères suivis de 3 caractères supplémentaires (autrement dit, vous ne voulez que les 10 premiers caractères), vous pouvez utiliser
script | cut -c 1-10
Si elle génère un nombre incertain de caractères non-espace, suivis d'un espace et de 2 autres caractères supplémentaires (en d'autres termes, vous ne voulez que le premier champ), vous pouvez utiliser
script | cut -d ' ' -f 1
... comme dans le commentaire de majhool plus tôt. Selon votre plate-forme, vous pouvez également avoir colrm, ce qui, une fois de plus, fonctionnerait si les lignes avaient une longueur fixe:
script | colrm 11
Une autre réponse repose sur le dernier caractère étant un espace. Cela fonctionnera avec (presque) n'importe quel personnage dans cette position et le fera "SANS utiliser sed, ou Perl, etc.":
while read -r line
do
echo ${line:0:${#line}-3}
done
Si vos lignes sont de longueur fixe, remplacez echo
par:
echo ${line:0:9}
ou
printf "%.10s\n" "$line"
mais chacun de ceux-ci est certainement beaucoup plus lent que sed
.
Pas besoin de couper ou de magie, en bash vous pouvez couper une ficelle comme ceci:
ORGSTRING="123456"
CUTSTRING=${ORGSTRING:0:-3}
echo "The original string: $ORGSTRING"
echo "The new, shorter and faster string: $CUTSTRING"
Vous pouvez utiliser awk juste pour imprimer le premier "champ" s'il n'y a pas d'espaces (ou s'il y en aura, changez le séparateur ".
J'ai mis les champs que vous aviez ci-dessus dans un fichier et je l'ai fait
awk '{ print $1 }' < test.txt
1234567890
1234567891
Je ne sais pas si c'est mieux.
que voulez-vous dire ne veulent pas utiliser sed/awk à des fins de vitesse? sed/awk sont plus rapides que les boucles en lecture du shell pour le traitement des fichiers.
$ sed 's/[ \t]*\*$//' file
1234567890
1234567891
$ sed 's/..\*$//' file
1234567890
1234567891
avec bash shell
while read -r a b
do
echo $a
done <file