-
BASH > manipuler des mots / lignes / fichiers
GÉNÉRALITÉS SUR GREP SED AWK SORT
sed
sed -ipour écrire directement dans le fichiersed ‘///I‘ignore la cassesed "s///g;s///g"pour plusieurs remplacementsawk
awk -F’<h1>’pour dire que<h1>est le séparateurOn doit backslashé les slash :
awk -F’\/’pour dire que/est le séparateur.sort
sort -t" "Pour dire que la séparation entre les champs sera un espace. (\tpour une tabulation)Optimisation et vitesse d’exécution
la substitution sera plus rapide si on recherche la chaîne avant de faire la substitution
s/…/…/:sed ‘s/foo/bar/g’ fichier# commande normale de substitutionsed ‘/foo/ s/foo/bar/g’ fichier# exécution plus rapidesed ‘/foo/ s//bar/g’ fichier# raccourci de syntaxeSi vous devez altérer ou écraser seulement une section d’un fichier et que vous voulez seulement une sortie pour une première partie d’un fichier quelconque, la commande "quit" (
q) dans le script réduira considérablement le temps d’exécution pour de gros fichiers. Donc:sed -n ’45,50p’ fichier# imprimez les lignes nos. 45-50 d’un fichiersed -n ’51q;45,50p’ fichier# idem, mais bien plus viteAFFICHER / AJOUTER / SUPPRIMER DES LIGNES
Afficher des lignes
les 10 premières lignes :
head -10oused 10qouawk ‘NR<11‘la 1ère ligne :
head -1oused qles 10 dernières lignes :
tailoused -e :a -e ‘$q;N;11,$D;ba’les 2 dernières lignes :
tail -2oused ‘$!N;$!D’la DERNIERE ligne d’un fichier :
tail -1oused ‘$!d’oused -n ‘$p’l’AVANT-DERNIERE ligne d’un fichier :
sed -e ‘$!{h;d;}’ -e x(pour fichiers d’une ligne , affiche une ligne vide)sed -e ’1{$q;}’ -e ‘$!{h;d;}’ -e x(pour fichiers d’une ligne , affiche la ligne)sed -e ’1{$d;}’ -e ‘$!{h;d;}’ -e x(pour fichiers d’une ligne , n’affiche rien)la ligne 52 :
sed -n ‘52p’oused ‘52!d’oused ‘52q;d’(très efficace sur de gros fichiers)les lignes 2 à 4 :
sed -n ‘2,4p’oused ’2,4!d’ouawk ‘NR==2,NR==4‘les lignes coïncidant avec regex :
sed -n ‘/regex/p’oused ‘/regex/!d’ouegrep "(regex)"les lignes NE coïncidant PAS avec regex :
grep -v regexoused ‘/regex/d’3 lignes après celle matchant avec regex :
sed -n "/regex/,+3"la ligne PRÉCÉDANT celle coïncidant avec regex, mais pas celle coïncidant avec regex :
sed -n ‘/regex/{g;1!p;};h’la ligne SUIVANT celle qui coïncide avec regex, mais pas la ligne coïncidant avec regex :
sed -n ‘/regex/{n;p;}’la ligne SUIVANT celle qui coïncide avec regex, ET la ligne coïncidant avec regex :
sed -n ‘/regex/{N;p;}’une ligne avant et après regex, avec numérotation de lignes :
grep -A1 -B1 regexoused -n -e ‘/regex/{=;x;1!p;g;$!N;p;D;}’ -e hDEPUIS regex jusqu’à la fin du fichier :
sed -n ‘/regexp/,$p’De regex1 à regex2 inclus :
sed -n ‘/regex1/,/regex2/p’paragraphe si il contient AAA (paragraphes séparés par des lignes vides) HHsed v1.5 veuillez insérer un ‘G;’ après ‘x;’ dans les trois commandes ci-bas
sed -e ‘/./{H;$!d;}’ -e ‘x;/AAA/!d;’le paragraphe s’il contient AA et BB et CC (peu importe l’ordre)
sed -e ‘/./{H;$!d;}’ -e ‘x;/AA/!d;/BB/!d;/CC/!d’le paragraphe s’il contient AA ou BB ou CC
sed -e ‘/./{H;$!d;}’ -e ‘x;/AA/b’ -e ‘/BB/b’ -e ‘/CC/b’ -e dles lignes de plus de 65 caractères :
sed -n ‘/^.\{65\}/p’ouawk ‘length>65‘les lignes de moins de 65 caractères :
sed -n ‘/^.\{65\}/!p’oused ‘/^.\{65\}/d’ouawk ‘length < 65′les lignes impaires :
awk ‘NR%2′les lignes paires :
awk ‘!(NR%2)’les lignes IMPAIRES SAUF la 1ère et la 3ème :
sed -n ‘1,3!p;n’à partir de la 3ème ligne, affiche chaque septième ligne :
sed -n ‘3,${p;n;n;n;n;n;n;}’le DERNIER MOT de chaque ligne :
awk ‘{print $NF}’Supprimer des lignes
supp les lignes coïncidant avec une regex :
sed ‘/regex/d’supp la section coïncidant avec DEUX regex :
sed ‘/regex1/,/regex2/d‘supp la ligne APRÈS une regex :
sed "/regex/{n;d}"supp les lignes consécutives identiques d’un fichier : (première ligne conservée, les autres éliminées) :
uniq- ou -sed ‘$!N; /^\(.*\)\n\1$/!P; D’supp les lignes identiques, non-consécutives, d’un fichier : Attention à ne pas dépasser la mémoire tampon
sed -n ‘G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P’supp TOUTES les lignes SAUF celles en duplicata :
uniq -doused ‘$!N; s/^\(.*\)\n\1$/\1/; t; D’supp les 10 premières lignes d’un fichier :
sed ’1,10d’supp la dernière ligne d’un fichier :
sed ‘$d’supp les deux dernières lignes d’un fichier :
sed ‘N;$!P;$!D;$d’ouhead -n -2supp les 10 dernières lignes d’un fichier :
head -n -10supp la DERNIERE ligne :
sed ‘$d’supp chaque huitième ligne :
sed ‘n;n;n;n;n;n;n;d;’supp TOUTES les lignes vides d’un fichier (
grep ‘.’) :sed ‘/^$/d’oused ‘/./!d’supp les lignes vides CONSÉCUTIVES sauf les deux premières:
sed ‘/^$/N;/\n$/N;//D’supp les lignes vides au début d’un fichier :
sed ‘/./,$!d’supp les lignes vides de la fin d’un fichier :
sed -e :a -e ‘/^\n*$/{$d;N;ba’ -e ‘}’supp les lignes vides du début et de la fin d’un fichier :
cat -ssupp les lignes vides consécutives (sauf la première) :
sed ‘/./,/^$/!d’(0 ligne vide au début, 1 à la fin)sed ‘/^$/N;/\n$/D’(1 ligne vide au début, 0 à la fin)supp la dernière ligne de chaque paragraphe :
sed -n ‘/^$/{p;h;};/./{x;/./p;}’Supp les lignes de A.txt si présente dans B.txt :
grep -F -x -v -f B.txt A.txt > C.txtAjouter des lignes
ajouter une ligne VIDE entre chaque ligne :
sed Gahouter 2 lignes VIDES entre chaque ligne :
sed ‘G;G’ajouter une ligne vide entre chaque ligne sans avoir 2 lignes vides consécutives :
sed ‘G;G’ajouter une ligne vide au-dessus de chaque ligne qui contient regex :
sed ‘/regex/{x;p;x;}’ajouter une ligne vide en-dessous de chaque ligne qui contient regex :
sed ‘/regex/G’ajouter une ligne vide au-dessus et en-dessous de chaque ligne qui contient "regex" :
sed ‘/regex/{x;p;x;G;}’ajouter blabla après la ligne contenant regex :
sed ‘/regex/a blabla‘AJOUTER / SUPPRIMER / REMPLACER DU TEXTE
Ajouter du texte
ajouter 5 espaces au début de chaque ligne :
sed ‘s/^/ /g’ajouter un crochet et espace à chaque ligne (citer un message) :
sed ‘s/^/> /’ajouter toto à la FIN de chaque ligne :
sed ‘s/$/toto/g’Supprimer du texte
supp le double-espacement (supposons les lignes paires comme étant vides) :
sed ‘n;d’supp tout espace (espace, tab) à la gauche de chaque ligne, et appuyer le résultat à la marge gauche :
sed ‘s/^[ \t]*//’supp tout espace (espace, tab) à la fin de chaque ligne :
sed ‘s/[ \t]*$//’supp tout espace des deux bouts de chaque ligne :
sed ‘s/^[ \t]*//;s/[ \t]*$//’supp les sauts de ligne :
tr -d '\n'
- ou -sed ':a;N;$!ba;s/\n//g' fichier
supp le crochet et espace précédant chaque ligne (enlever la citation du message) :
sed ‘s/^> //’supp les balises HTML :
sed -e :a -e ‘s/<[^>]*>//g;/</N;//ba’Remplacer du texte
remplacer le PREMIER foo de CHAQUE ligne par bar :
sed ‘s/foo/bar/’remplacer le PREMIER foo de la PREMIERE ligne par bar :
sed ‘0,/foo/s/foo/bar/’remplacer le TROISIEME foo de CHAQUE ligne :
sed ‘s/foo/bar/3‘remplacer TOUS les foo par bar :
sed ‘s/foo/bar/g‘remplacer TOUS les foo par bar sur la 2ème ligne :
sed ’0,/foo/! {0,/foo/ s/foo/bar/g}’remplacer TOUS les foo par bar sur la 3ème ligne :
awk ‘/foo/{c++;if(c==3){gsub("foo","bar")}}1′remplacer l’AVANT-DERNIER foo par bar :
sed ‘s/\(.*\)foo\(.*foo\)/\1bar\2/’remplacer le DERNIER foo par bar :
sed ‘s/\(.*\)foo/\1bar/’remplacer foo par bar SEULEMENT pour les lignes contenant toto :
sed ‘/toto/s/foo/bar/g’remplacer foo par bar SAUF la 1ère ligne trouvée :
sed ‘0,/foo/! s/foo/bar/g’remplacer foo par bar A L’EXCEPTION des lignes contenant baz :
sed ‘/baz/!s/foo/bar/g’remplacer toto ou tata ou titi par foo :
sed ‘s/toto/foo/g;s/tata/foo/g;s/titi/foo/g’remplacer tous les
\n
par un vrai saut de ligne :sed 's/\\n/\n/g'
remplacer les sauts de ligne par un tiret :
tr '\n' '-'
remplacer les points par un saut de ligne :
tr -s ‘.‘ ‘\n‘(-spour supp les lignes vides)remplacer les sauts de ligne à la fin, par un espace :
tr "\n$" "\ "remplacer le PREMIER MOT de chaque ligne par toto :
awk ‘{$1="toto";print}’VOIR LES CARACTÈRES INVISIBLES (les échappements, par exemple) :
tr -doucat -vtpermet d’afficher les caractères suivants :
^@^A^B^C^D^E^F^G^H^I^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\^]^^^_ !"#$%&'()*+,-./0123456789 :;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~^?M-^@M-^AM-^BM-^CM -^DM-^EM-^FM-^GM-^HM-^IM-M-^KM-^LM-^MM-^NM-^OM-^PM-^QM-^RM-^SM-^TM-^UM-^VM-^WM-^XM-^YM- ^ZM-^[M-^\M-^]M-^^M-^_M- M-!M-"M-#M-$M-%M-&M-'M-(M-)M-*M-+M-,M--M-.M-/M-0M-1M-2M-3M-4M- 5M-6M-7M-8M-9M-:M-;M-<M-=M->M-?M-@M-AM-BM-CM-DM-EM-FM-GM-HM-IM-JM-KM-LM-MM-NM-OM-PM-QM- RM-SM-TM-UM-VM-WM-XM-YM-ZM-[M-\M-]M-^M-_M-`M-aM-bM-cM-dM-eM-fM-gM-hM-iM-jM-kM-lM-mM-nM- oM-pM-qM-rM-sM-tM-uM-vM-wM-xM-yM-zM-{M-|M-}M-~M-^?
NUMÉROTER ET COMPTER
Numéroter chaque ligne du fichier (à gauche) :
L’utilisation d’une tabulation (
\t) au lieu d’un espace préservera les margessed = fichier | sed ‘N;s/\n/\t/’
Numérote chaque ligne (numéro à gauche, appuyé à droite)
sed = fichier | sed ‘N; s/^/ /; s/ *\(.\{6,\}\)\n/\1 /’Numérote chaque ligne, mais affiche le numéro de ligne si la ligne n’est pas vide :
sed ‘/./=’ fichier | sed ‘/./N; s/\n/ /’Compter les lignes :
wc -l- ou -sed -n ‘$=’Nombre de fois où le motif est trouvé :
grep -c ‘motif’ !$Générer une suite de nombre :
seq 10→1 2 3 4 5 6 7 8 9 10seq 10 15→10 11 12 13 14 15Crée 7 nombres aléatoires entre 0 et 100 inclus :
awk ‘BEGIN { for (i = 1; i <= 7; i++) print int(101 * rand()) }’Longueur de la ligne la plus longue :
awk ‘{ if (length($0) > max) max = length($0) } END { print max }’ dataNombre total d’octets utilisés par FILES :
ls -lg FILES | awk ‘{ x += $5 } ; END { print "octets: " x }’Liste the average file size of all .PNG files within a directory:
ls -l *.png | gawk ‘{sum += $5; n++;} END {print sum/n;}’Liste des login de tous les users :
awk -F: ‘{ print $1 }’ /etc/passwd | sortCONVERSION UNIX <-> DOS
DOS vers UNIX
Il suffit d’effacer le caractère "
\r" (retour chariot) de fin de ligne représenté symboliquement par "^M"Première solution : SED
sed 's/^M$//' fichier
Avec GNU-sed (gsed 3.02.80 et supérieur), on peut utiliser la notation ASCII :
sed ‘s/\x0D$//’ fichierDeuxième solution : AWK
awk ‘{ sub(/\r$/,""); print }’On utilise la fonction
sub(regex, remp, chaine). sub remplace la première instance de regex par remp dans la chaine. Si chaine n’est pas renseignée, alors cela est valable pour la ligne entière ($0)Troisième solution : TR
tr -d \rUNIX vers DOS
Il suffit de faire le contraire de la commande précédente, à savoir : insérer le caractère "
^M" :sed 's/$/^M/' fichier
Deuxième méthode :
awk ‘{ sub(/$/,"\r"); print }’ORDONNER ET TRIER
renverser l’ordre des lignes :
tac fichieroused -n ’1!G;h;$p’renverse l’ordre de chaque caractère sur une ligne :
revoused ‘/\n/!G;s/\(.\)\(.*\n\)/&\2\1/;//D;s/.//’insérer des virgules aux nombres ( 1234567 → 1,234,567 ) :
sed -e :a -e ‘s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/;ta’Trier sur une colonne spécifique :
sort -t";" -k2 fichier(champs espacés par;2ème champs)décimaux et signes négatifs :
gsed -r ‘:a;s/(^|[^0-9.])([0-9]+)([0-9]{3})/\1\2,\3/g;ta’ajouter une ligne blanche toutes les cinq lignes (après lignes 5, 10, 15, 20) :
sed ‘n;n;n;n;G;’grep AAA et BBB et CCC (peu importe l’ordre) :
sed ‘/AAA/!d; /BBB/!d; /CCC/!d’grep AAA et BBB et CCC (dans cet ordre) :
sed ‘/AAA.*BBB.*CCC/!d’grep AAA ou BBB ou CCC ("egrep") :
sed -e ‘/AAA/b’ -e ‘/BBB/b’ -e ‘/CCC/b’ -e daligner le texte à droite sur la 79ème colonne :
sed -e :a -e ‘s/^.\{1,78\}$/ &/;ta’(mettre à 78 plus un espace)centrer le texte sur le centre de la 79e colonne :
sed -e :a -e ‘s/^.\{1,77\}$/ & /;ta’(espaces ajoutés à la fin de la ligne)sed -e :a -e ‘s/^.\{1,77\}$/ &/;ta’ -e ‘s/\( *\)\1/\1/’(aucun espace ajouté à la fin de la ligne)Trier les paragraphes par ordre alphabétique. Les paragraphes sont séparés par des lignes vides. sed utilise \v comme tabulation verticale, ou n’importe lequel caractère unique peut servir.
sed ‘/./{H;d;};x;s/\n/={NL}=/g’ file | sort | sed ’1s/={NL}=//;s/={NL}=/\n/g’gsed ‘/./{H;d};x;y/\n/\v/’ file | sort | sed ’1s/\v//;y/\v/\n/’——- NOTES ——-
SED :
sed -e ‘/AAA/b’ -e ‘/BBB/b’ -e ‘/CCC/b’ -e dGNU sed :
sed ‘/AAA\|BBB\|CCC/b;d’JOINDRE, SÉPARER
joindre des paires de lignes ensemble côte-à-côte (émule
paste) :sed ‘$!N;s/\n/ /’si une ligne se termine par
\, joindre la ligne suivante à la présente :sed -e :a -e ‘/\\$/N; s/\\\n//; ta’si une ligne débute par
=, l’ajouter à la précédente et remplacer le symbole "=" par un espace simple :sed -e :a -e ‘$!N;s/\n=/ /;ta’ -e ‘P;D’———
Effacer les blancs en début de ligne
awk ‘{ sub(/^[ \t]+/, ""); print }’
sub() replace regular expression^[ \t]+with nothing"".^[ \t]+means - match one or more space " " or a tab "\t" at the beginning "^" of the string.Effacer les blancs en fin de ligne
awk ‘{ sub(/[ \t]+$/, ""); print }’
It replaces regular expression "[ \t]+$" with nothing.[ \t]+$means - match one or more space " " or a tab "\t" at the end "$" of the string. "+" means "one or more".Effacer les blancs en début et fin de ligne
awk ‘{ gsub(/^[ \t]+|[ \t]+$/, ""); print }’
gsub() does the same as sub(), except it performs as many substitutions as possible (that is, it’s a global sub()). For example, given a variable f = "foo", sub("o", "x", f) would replace just one "o" in variable f with "x", making f be "fxo"; but gsub("o", "x", f) would replace both "o"s in "foo" resulting "fxx".
The one-liner combines both previous one-liners - it replaces leading whitespace "^[ \t]+" and trailing whitespace "[ \t]+$" with nothing, thus trimming the string.To remove whitespace between fields you may use this one-liner:
awk ‘{ $1=$1; print }’
This is a pretty tricky one-liner. It seems to do nothing, right? Assign $1 to $1. But no, when you change a field, Awk rebuilds the $0 variable. It takes all the fields and concats them, separated by OFS (single space by default). All the whitespace between fields is gone.28. Insert 5 blank spaces at beginning of each line.
awk ‘{ sub(/^/, " "); print }’
remplace le début de la chaine "^".Substitute (find and replace) "foo" with "bar" on each line.
awk ‘{ sub(/foo/,"bar"); print }’
This one-liner is very similar to the others we have seen before. It uses the sub() function to replace "foo" with "bar". Please note that it replaces just the first match. To replace all "foo"s with "bar"s use the gsub() function:
awk ‘{ gsub(/foo/,"bar"); print }’
Another way is to use the gensub() function:
gawk ‘{ $0 = gensub(/foo/,"bar",4); print }’
This one-liner replaces only the 4th match of "foo" with "bar". It uses a never before seen gensub() function. The prototype of this function is gensub(regex, s, h[, t]). It searches the string "t" for "regex" and replaces "h"-th match with "s". If "t" is not given, $0 is assumed. Unlike sub() and gsub() it returns the modified string "t" (sub and gsub modified the string in-place).
Gensub() is a non-standard function and requires GNU Awk or Awk included in NetBSD.
In this one-liner regex = "/foo/", s = "bar", h = 4, and t = $0. It replaces the 4th instance of "foo" with "bar" and assigns the new string back to the whole line $0.32. Substitute "foo" with "bar" only on lines that contain "baz".
awk ‘/baz/ { gsub(/foo/, "bar") }; { print }’
As I explained in the first one-liner in the first part of the article, every Awk program consists of a sequence of pattern-action statements "pattern { action statements }". Action statements are applied only to lines that match pattern.
In this one-liner the pattern is a regular expression /baz/. If line contains "baz", the action statement gsub(/foo/, "bar") is executed. And as we have learned, it substitutes all instances of "foo" with "bar". If you want to substitute just one, use the sub() function!33. Substitute "foo" with "bar" only on lines that do not contain "baz".
awk ‘!/baz/ { gsub(/foo/, "bar") }; { print }’
This one-liner negates the pattern /baz/. It works exactly the same way as the previous one, except it operates on lines that do not contain match this pattern.34. Change "scarlet" or "ruby" or "puce" to "red".
awk ‘{ gsub(/scarlet|ruby|puce/, "red"); print}’
This one-liner makes use of extended regular expression alternation operator | (pipe). The regular expression /scarlet|ruby|puce/ says: match "scarlet" or "ruby" or "puce". If the line matches, gsub() replaces all the matches with "red".35. Reverse order of lines (emulate "tac").
awk ‘{ a[i++] = $0 } END { for (j=i-1; j>=0;) print a[j--] }’
This is the trickiest one-liner today. It starts by recording all the lines in the array "a". For example, if the input to this program was three lines "foo", "bar", and "baz", then the array "a" would contain the following values: a[0] = "foo", a[1] = "bar", and a[2] = "baz".
When the program has finished processing all lines, Awk executes the END { } block. The END block loops over the elements in the array "a" and prints the recorded lines. In our example with "foo", "bar", "baz" the END block does the following:
for (j = 2; j >= 0; ) print a[j--]
First it prints out j[2], then j[1] and then j[0]. The output is three separate lines "baz", "bar" and "foo". As you can see the input was reversed.36. Join a line ending with a backslash with the next line.
awk ‘/\\$/ { sub(/\\$/,""); getline t; print $0 t; next }; 1′
This one-liner uses regular expression "/\\$/" to look for lines ending with a backslash. If the line ends with a backslash, the backslash gets removed by sub(/\\$/,"") function. Then the "getline t" function is executed. "Getline t" reads the next line from input and stores it in variable t. "Print $0 t" statement prints the original line (but with trailing backslash removed) and the newly read line (which was stored in variable t). Awk then continues with the next line. If the line does not end with a backslash, Awk just prints it out with "1″.
Unfortunately this one liner fails to join more than 2 lines (this is left as an exercise to the reader to come up with a one-liner that joins arbitrary number of lines that end with backslash :)).37. Print and sort the login names of all users.
awk -F ":" ‘{ print $1 | "sort" }’ /etc/passwd
This is the first time we see the -F argument passed to Awk. This argument specifies a character, a string or a regular expression that will be used to split the line into fields ($1, $2, …). For example, if the line is "foo-bar-baz" and -F is "-", then the line will be split into three fields: $1 = "foo", $2 = "bar" and $3 = "baz". If -F is not set to anything, the line will contain just one field $1 = "foo-bar-baz".Specifying -F is the same as setting the FS (Field Separator) variable in the BEGIN block of Awk program:
awk -F ":"
# is the same as
awk ‘BEGIN { FS=":" }’
/etc/passwd is a text file, that contains a list of the system’s accounts, along with some useful information like login name, user ID, group ID, home directory, shell, etc. The entries in the file are separated by a colon ":".
Here is an example of a line from /etc/passwd file:
pkrumins:x:1000:100:Peteris Krumins:/home/pkrumins:/bin/bash
If we split this line on ":", the first field is the username (pkrumins in this example). The one-liner does just that - it splits the line on ":", then forks the "sort" program and feeds it all the usernames, one by one. After Awk has finished processing the input, sort program sorts the usernames and outputs them.38. Print the first two fields in reverse order on each line.
awk ‘{ print $2, $1 }’ file
This one liner is obvious. It reverses the order of fields $1 and $2. For example, if the input line is "foo bar", then after running this program the output will be "bar foo".39. Swap first field with second on every line.
awk ‘{ temp = $1; $1 = $2; $2 = temp; print }’
This one-liner uses a temporary variable called "temp". It assigns the first field $1 to "temp", then it assigns the second field to the first field and finally it assigns "temp" to $2. This procedure swaps the first two fields on every line. For example, if the input is "foo bar baz", then the output will be "bar foo baz".
Ps. This one-liner was incorrect in Eric’s awk1line.txt file. "Print" was missing.40. Delete the second field on each line.
awk ‘{ $2 = ""; print }’
This one liner just assigns empty string to the second field. It’s gone.41. Print the fields in reverse order on every line.
awk ‘{ for (i=NF; i>0; i--) printf("%s ", $i); printf ("\n") }’
We saw the "NF" variable that stands for Number of Fields in the part one of this article. After processing each line, Awk sets the NF variable to number of fields found on that line.
This one-liner loops in reverse order starting from NF to 1 and outputs the fields one by one. It starts with field $NF, then $(NF-1), …, $1. After that it prints a newline character.42. Remove duplicate, consecutive lines (emulate "uniq")
awk ‘a !~ $0; { a = $0 }’
Variables in Awk don’t need to be initialized or declared before they are being used. They come into existence the first time they are used. This one-liner uses variable "a" to keep the last line seen "{ a = $0 }". Upon reading the next line, it compares if the previous line (in variable "a") is not the same as the current one "a !~ $0″. If it is not the same, the expression evaluates to 1 (true), and as I explained earlier, any true expression is the same as "{ print }", so the line gets printed out. Then the program saves the current line in variable "a" again and the same process continues over and over again.
This one-liner is actually incorrect. It uses a regular expression matching operator "!~". If the previous line was something like "fooz" and the new one is "foo", then it won’t get output, even though they are not duplicate lines.Here is the correct, fixed, one-liner:
awk ‘a != $0; { a = $0 }’
It compares lines line-wise and not as a regular expression.Effacer des lignes identiques, mais non consécutives.
awk ‘!a[$0]++’
It registers the lines seen in the associative-array "a" and at the same time tests if it had seen the line before. If it had seen the line before, thena[line]>0and!a[line] == 0.Toute expression valant false n’est pas prise en compte, alors que toute expression valant true équivaut à
{ print }.Exemple :
foo
bar
foo
baz1) awk voit pour la première fois "
foo".- awk évalue "
!a["foo"]++" : "a["foo"]" vaut false (pas d’éléments dans le tableau), donc "!a["foo"]" vaut true.- awk fait donc un print pour "
foo".- awk incrémente "
a["foo"]" de 1 grâce à "++".- Le tableau "
a" contient donc maintenant la valeur "a["foo"] == 1".
2) awk voit maintenant bar.- Il fait de même que (1).
- Nous avons donc maintenant : "
a["foo"] == 1" et "a["bar"] == 1".
3) Puis awk voit pour la deuxième fois "foo".- Cette fois, "
a["foo"]" vaut true, donc "!a["foo"]" vaut false- awk ne fait donc rien !
- Le tableau
areste donc identique : "a["foo"] == 2" et "a["bar"] == 1".
4) etc.The output:
foo
bar
bazUne autre manière de faire :
awk ‘!($0 in a) { a[$0]; print }’Concatenate every 5 lines of input with a comma.
awk ‘ORS=NR%5?",":"\n"‘
si ORS = 0 (modulo 5) alors on ajoute un saut de ligne (\n), sinon on ajoute une virgule (,)awk ‘ORS=NR%5?"FALSE":"TRUE"‘45. Afficher les 15 premières lignes
awk ‘NR < 16′
NR = Number Records = Numéro de l’enregistrement en cours.47. Afficher les 2 dernières lignes.
tail -248. Afficher la dernière ligne
tail -1
awk ‘END { print }’49. Print only the lines that match a regular expression "/regex/" (emulates "grep").
awk ‘/regex/’50. Print only the lines that do not match a regular expression "/regex/" (emulates "grep -v").
awk ‘!/regex/’51. Print the line immediately before a line that matches "/regex/" (but not the line that matches itself).
awk ‘/regex/ { print x }; { x=$0 }’
This one-liner always saves the current line in the variable "x". When it reads in the next line, the previous line is still available in the "x" variable. If that line matches "/regex/", it prints out the variable x, and as a result, the previous line gets printed.It does not work, if the first line of the file matches "/regex/", in that case, we might want to print "match on line 1″, for example:
awk ‘/regex/ { print (x=="" ? "match on line 1″ : x) }; { x=$0 }’
This one-liner tests if variable "x" contains something. The only time that x is empty is at very first line. In that case "match on line 1″ gets printed. Otherwise variable "x" gets printed (that as we found out contains the previous line). Notice that this one-liner uses a ternary operator "foo?bar:baz" that is short for "if foo, then bar, else baz".52. Print the line immediately after a line that matches "/regex/" (but not the line that matches itself).
awk ‘/regex/ { getline; print }’
This one-liner calls the "getline" function on all the lines that match "/regex/". This function sets $0 to the next line (and also updates NF, NR, FNR variables). The "print" statement then prints this next line. As a result, only the line after a line matching "/regex/" gets printed. If it is the last line that matches "/regex/", then "getline" actually returns error and does not set $0. In this case the last line gets printed itself.53. Print lines that match any of "AAA" or "BBB", or "CCC".
awk ‘/AAA|BBB|CCC/’
This one-liner uses a feature of extended regular expressions that support the | or alternation meta-character. This meta-character separates "AAA" from "BBB", and from "CCC", and tries to match them separately on each line. Only the lines that contain one (or more) of them get matched and printed.54. Print lines that contain "AAA" puis "BBB", puis "CCC" dans cet ordre.
awk ‘/AAA.*BBB.*CCC/’
This one-liner uses a regular expression "AAA.*BBB.*CCC" to print lines. This regular expression says, "match lines containing AAA followed by any text, followed by BBB, followed by any text, followed by CCC in this order!" If a line matches, it gets printed.55. Print only the lines that are 65 characters in length or longer.
awk ‘length > 64′56. Print only the lines that are less than 64 characters in length.
awk ‘length < 64′57. Print a section of file from regular expression to end of file.
awk ‘/regex/,0′
This one-liner uses a pattern match in form ‘pattern1, pattern2′ that is called "range pattern". The 3rd Awk Tip from article "10 Awk Tips, Tricks and Pitfalls" explains this match very carefully. It matches all the lines starting with a line that matches "pattern1″ and continuing until a line matches "pattern2″ (inclusive). In this one-liner "pattern1″ is a regular expression "/regex/" and "pattern2″ is just 0 (false). So this one-liner prints all lines starting from a line that matches "/regex/" continuing to end-of-file (because 0 is always false, and "pattern2″ never matches).58. Afficher les lignes 8 à 12 (incluses).
awk ‘NR==8,NR==12′
On utilise le format "pattern1, pattern2″. The "pattern1″ here is "NR==8″ and "pattern2″ is "NR==12″. The first pattern means "the current line is 8th" and the second pattern means "the current line is 12th". This one-liner prints lines between these two patterns.59. Print line number 52.
awk ‘NR==52 { print; exit }’60. Print section of a file between two regular expressions (inclusive).
awk ‘/Iowa/,/Montana/’
I explained what a range pattern such as "pattern1,pattern2″ does in general in one-liner #57. In this one-liner "pattern1″ is "/Iowa/" and "pattern2″ is "/Montana/". Both of these patterns are regular expressions. This one-liner prints all the lines starting with a line that matches "Iowa" and ending with a line that matches "Montana" (inclusive).61. Delete all blank lines from a file.
awk NF
awk ‘/./’—
a) Print all records from some pattern:
awk ‘/pattern/{f=1}f’ fileb) Print all records after some pattern:
awk ‘f;/pattern/{f=1}’ filec) Print the Nth record after some pattern:
awk ‘c&&!--c;/pattern/{c=N}’ filed) Print every record except the Nth record after some pattern:
awk ‘c&&!--c{next}/pattern/{c=N}’ filee) Print the N records after some pattern:
awk ‘c&&c--;/pattern/{c=N}’ filef) Print every record except the N records after some pattern:
awk ‘c&&c--{next}/pattern/{c=N}’ file—————————————————————————————--
Sometimes, you want to operate only on some lines of the input (according to some condition), but also want to print all the lines, regardless of whether they were affected by your operation or not.
A typical example is a program like this:
awk ‘{sub(/pattern/,"foobar")}1′
This tries to replace "pattern" with "foobar".
Whether or not the substitution succeeds, the always-true condition "1″ prints each line.
This results in a program that does the same job as sed ‘s/pattern/foobar/’. Here are some examples of typical awk idioms, using only conditions:awk ‘NR % 6′# prints all lines except those divisible by 6
awk ‘NR > 5′# prints from line 6 onwards (like tail -n +6, or sed ’1,5d’)
awk ‘$2 == "foo"‘# prints lines where the second field is "foo"
awk ‘NF >= 6′# prints lines with 6 or more fields
awk ‘/foo/ && /bar/’# prints lines that match /foo/ and /bar/, in any order
awk ‘/foo/ && !/bar/’# prints lines that match /foo/ but not /bar/
awk ‘/foo/ || /bar/’# prints lines that match /foo/ or /bar/ (like grep -e ‘foo’ -e ‘bar’)
awk ‘/foo/,/bar/’# prints from line matching /foo/ to line matching /bar/, inclusive
awk ‘NF’# prints only nonempty lines (or: removes empty lines, where NF==0)
awk ‘NF--’# removes last field and prints the line
awk ‘$0 = NR" "$0′# prepends line numbers (assignments are valid in conditions)Another construct that is often used in awk is as follows:
awk ‘NR==FNR { # some actions; next} # other condition {# other actions}’ file1 file2
This is used when processing two files. When processing more than one file, awk reads each file sequentially, one after another, in the order they are specified on the command line.
The special variable NR stores the total number of input records read so far, regardless of how many files have been read. The value of NR starts at 1 and always increases until the program terminates. Another variable, FNR, stores the number of records read from the current file being processed. The value of FNR starts from 1, increases until the end of the current file, starts again from 1 as soon as the first line of the next file is read, and so on. So, the condition "NR==FNR" is only true while awk is reading the first file. Thus, in the program above, the actions indicated by "# some actions" are executed when awk is reading the first file; the actions indicated by "# other actions" are executed when awk is reading the second file, if the condition in "# other condition" is met. The "next" at the end of the first action block is needed to prevent the condition in "# other condition" from being evaluated, and the actions in "# other actions" from being executed while awk is reading the first file.There are really many problems that involve two files that can be solved using this technique. Here are some examples:
# prints lines that are both in file1 and file2 (intersection)
awk ‘NR==FNR{a[$0];next} $0 in a’ file1 file2Here we see another typical idiom: a[$0] has the only purpose of creating the array element indexed by $0. During the pass over the first file, all the lines seen are remembered as indexes of the array a. The pass over the second file just has to check whether each line being read exists as an index in the array a (that’s what the condition $0 in a does). If the condition is true, the line is printed (as we already know).
Another example. Suppose we have a data file like this
20081010 1123 xxx
20081011 1234 def
20081012 0933 xyz
20081013 0512 abc
20081013 0717 def
…thousand of lines…where "xxx", "def", etc. are operation codes. We want to replace each operation code with its description. We have another file that maps operation codes to human readable descriptions, like this:
abc withdrawal
def payment
xyz deposit
xxx balance
…other codes…We can easily replace the opcodes in the data file with this simple awk program, that again uses the two-files idiom:
# use information from a map file to modify a data file
awk ‘NR==FNR{a[$1]=$2;next} {$3=a[$3]}1′ mapfile datafileFirst, the array a, indexed by opcode, is populated with the human readable descriptions. Then, it is used during the reading of the second file to do the replacements. Each line of the datafile is then printed after the substitution has been made.
Another case where the two-files idiom is useful is when you have to read the same file twice, the first time to get some information that can be correctly defined only by reading the whole file, and the second time to process the file using that information. For example, you want to replace each number in a list of numbers with its difference from the largest number in the list:
# replace each number with its difference from the maximum
awk ‘NR==FNR{if($0>max) max=$0;next} {$0=max-$0}1′ file fileNote that we specify "file file" on the command line, so the file will be read twice.
Caveat: all the programs that use the two-files idiom will not work correctly if the first file is empty (in that case, awk will execute the actions associated to NR==FNR while reading the second file). To correct that, you can reinforce the NR==FNR condition by adding a test that checks that also FILENAME equals ARGV[1].
Pitfall: shorten pipelinesIt’s not uncommon to see lines in scripts that look like this:
somecommand | head -n +1 | grep foo | sed ‘s/foo/bar/’ | tr ‘[a-z]‘ ‘[A-Z]‘ | cut -d ‘ ‘ -f 2This is just an example. In many cases, you can use awk to replace parts of the pipeline, or even all of it:
somecommand | awk ‘NR>1 && /foo/{sub(/foo/,"bar"); print toupper($2)}’It would be nice to collect here many examples of pipelines that could be partially or completely eliminated using awk.
Print lines using rangesYes, we all know that awk has builtin support for range expressions, like
# prints lines from /beginpat/ to /endpat/, inclusive
awk ‘/beginpat/,/endpat/’Sometimes however, we need a bit more flexibility. We might want to print lines between two patterns, but excluding the patterns themselves. Or only including one. A way is to use these:
# prints lines from /beginpat/ to /endpat/, not inclusive
awk ‘/beginpat/,/endpat/{if (!/beginpat/&&!/endpat/)print}’# prints lines from /beginpat/ to /endpat/, not including /beginpat/
awk ‘/beginpat/,/endpat/{if (!/beginpat/)print}’It’s easy to see that there must be a better way to do that, and in fact there is. We can use a flag to keep track of whether we are currently inside the interesting range or not, and print lines based on the value of the flag. Let’s see how it’s done:
# prints lines from /beginpat/ to /endpat/, not inclusive
awk ‘/endpat/{p=0};p;/beginpat/{p=1}’# prints lines from /beginpat/ to /endpat/, excluding /endpat/
awk ‘/endpat/{p=0} /beginpat/{p=1} p’# prints lines from /beginpat/ to /endpat/, excluding /beginpat/
awk ‘p; /endpat/{p=0} /beginpat/{p=1}’All these programs just set p to 1 when /beginpat/ is seen, and set p to 0 when /endpat/ is seen. The crucial difference between them is where the bare "p" (the condition that triggers the printing of lines) is located. Depending on its position (at the beginning, in the middle, or at the end), different parts of the desired range are printed. To print the complete range (inclusive), you can just use the regular /beginpat/,/endpat/ expression or use the flag technique, but reversing the order of the conditions and associated patterns:
# prints lines from /beginpat/ to /endpat/, inclusive
awk ‘/beginpat/{p=1};p;/endpat/{p=0}’It goes without saying that while we are only printing lines here, the important thing is that we have a way of selecting lines within a range, so you can of course do anything you want instead of printing.
Split file on patternsSuppose we have a file like this
line1
line2
line3
line4
FOO1
line5
line6
FOO2
line7
line8
FOO3
line9
line10
line11
FOO4
line12
FOO5
line13We want to split this file on all the occurrences of lines that match /^FOO/, and create a series of files called, for example, out1, out2, etc. File out1 will contain the first 4 lines, out2 will contain "line5″ and "line6″, etc. There are at least two ways to do that with awk:
# first way, works with all versions of awk
awk -v n=1 ‘/^FOO[0-9]*/{close("out"n);n++;next} {print > "out"n}’ fileSince we don’t want to print anything when we see /^FOO/, but only update some administrative data, we use the "next" statement to tell awk to immediately start processing the next record. Lines that do not match /^FOO/ will instead be processed by the second block of code. Note that this method will not create empty files if an empty section is found (eg, if "FOO5\nFOO6″ is found, the file "out5″ will not be created). The "-v n=1″ is used to tell awk that the variable "n" should be initialized with a value of 1, so effectively the first output file will be called "out1″.
Another way (which however needs GNU awk to work) is to read one chunk of data at a time, and write that to its corresponding out file.
# another way, needs GNU awk
LC_ALL=C gawk -v RS=’FOO[0-9]*\n’ -v ORS= ‘{print > "out"NR}’ fileThe above code relies on the fact that GNU awk supports assigning a regular expression to RS (the standard only allows a single literal character or an empty RS). That way, awk reads a series of "records", separated by the regular expression matching /FOO[0-9]*\n/ (that is, the whole FOO… line). Since newlines are preserved in each section, we set ORS to empty since we don’t want awk to add another newline at the end of a block. This method does create an empty file if an empty section is encountered. On the downside, it’s a bit fragile because it will produce incorrect results if the regex used as RS appears somewhere else in the rest of the input.
We will see other examples where gawk’s support for regexes as RS is useful. Note that the last program used LC_ALL=C at the beginning…
Locale-based pitfallsSometimes awk can behave in an unexpected way if the locale is not C (or POSIX, which should be the same). See for example this input:
-rw-r--r-- 1 waldner users 46592 2003-09-12 09:41 file1
-rw-r--r-- 1 waldner users 11509 2008-10-07 17:42 file2
-rw-r--r-- 1 waldner users 11193 2008-10-07 17:41 file3
-rw-r--r-- 1 waldner users 19073 2008-10-07 17:45 file4
-rw-r--r-- 1 waldner users 36332 2008-10-07 17:03 file5
-rw-r--r-- 1 waldner users 33395 2008-10-07 16:53 file6
-rw-r--r-- 1 waldner users 54272 2008-09-18 16:20 file7
-rw-r--r-- 1 waldner users 20573 2008-10-07 17:50 file8You’ll recognize the familiar output of ls -l here. Let’s use a non-C locale, say, en_US.utf8, and try an apparently innocuous operation like removing the first 3 fields.
$ LC_ALL=en_US.utf8 awk --re-interval ‘{sub(/^([^[:space:]]+[[:space:]]+){3}/,"")}1′ file
-rw-r--r-- 1 waldner users 46592 2003-09-12 09:41 file1
-rw-r--r-- 1 waldner users 11509 2008-10-07 17:42 file2
-rw-r--r-- 1 waldner users 11193 2008-10-07 17:41 file3
-rw-r--r-- 1 waldner users 19073 2008-10-07 17:45 file4
-rw-r--r-- 1 waldner users 36332 2008-10-07 17:03 file5
-rw-r--r-- 1 waldner users 33395 2008-10-07 16:53 file6
-rw-r--r-- 1 waldner users 54272 2008-09-18 16:20 file7
-rw-r--r-- 1 waldner users 20573 2008-10-07 17:50 file8It looks like sub() did nothing. Now change that to use the C locale:
$ LC_ALL=C awk --re-interval ‘{sub(/^([^[:space:]]+[[:space:]]+){3}/,"")}1′ file
users 46592 2003-09-12 09:41 file1
users 11509 2008-10-07 17:42 file2
users 11193 2008-10-07 17:41 file3
users 19073 2008-10-07 17:45 file4
users 36332 2008-10-07 17:03 file5
users 33395 2008-10-07 16:53 file6
users 54272 2008-09-18 16:20 file7
users 20573 2008-10-07 17:50 file8Now it works. Another localization issue is the behavior of bracket expressions matching, like for example [a-z]:
$ echo ‘èòàù’ | LC_ALL=en_US.utf8 awk ‘/[a-z]/’
èòàùThis may or may not be what you want. When in doubt or when facing an apparently inexplicable result, try putting LC_ALL=C before your awk invocation.
Parse CSVUpdate: Mr. Waldner just notified me that he has improved this section of the article on his website. See "CVS Parsing With Awk".
This is another thing people do all the time with awk. Simple CSV files (with fields separated by commas, and commas cannot appear anywhere else) are easily parsed using FS=’,’. There can be spaces around fields, and we don’t want them, like eg
field1 , field2 , field3 , field4
Exploiting the fact that FS can be a regex, we could try something like FS=’^ *| *, *| *$’. This can be problematic for two reasons:
actual data field might end up correponding either to awk’s fields 1 … NF or 2 … NF, depending on whether the line has leading spaces or not;
for some reason, assigning that regex to FS produces unexpected results if fields have embedded spaces (anybody knows why?).In this case, it’s probably better to parse using FS=’,’ and remove leading and trailing spaces from each field:
# FS=’,’
for(i=1;i<=NF;i++){
gsub(/^ *| *$/,"",$i);
print "Field " i " is " $i;
}Another common CSV format is
"field1″,"field2″,"field3″,"field4″
Assuming double quotes cannot occur in fields. This is easily parsed using FS=’^"|","|"$’ (or FS=’","|"‘ if you like), keeping in mind that the actual fields will be in position 2, 3 … NF-1. We can extend that FS to allow for spaces around fields, like eg
"field1″ , "field2″, "field3″ , "field4″
by using FS=’^ *"|" *, *"|" *$’. Usable fields will still be in positions 2 … NF-1. Unlike the previous case, here that FS regex seems to work fine. You can of course also use FS=’,', and remove extra characters by hand:
# FS=’,’
for(i=1;i<=NF;i++){
gsub(/^ *"|" *$/,"",$i);
print "Field " i " is " $i;
}Another CSV format is similar to the first CSV format above, but allows for field to contain commas, provided that the field is quoted:
field1, "field2,with,commas" , field3 , "field4,foo"
We have a mixture of quoted and unquoted fields here, which cannot parsed directly by any value of FS (that I know of, at least). However, we can still get the fields using match() in a loop (and cheating a bit):
$0=$0″,"; # yes, cheating
while($0) {
match($0,/[^,]*,| *"[^"]*" *,/);
sf=f=substr($0,RSTART,RLENGTH); # save what matched in sf
gsub(/^ *"?|"? *,$/,"",f); # remove extra stuff
print "Field " ++c " is " f;
sub(sf,""); # "consume" what matched
}As the complexity of the format increases (for example when escaped quotes are allowed in fields), awk solutions become more fragile. Although I should not say this here, for anything more complex than the last example, I suggest using other tools (eg, Perl just to name one). Btw, it looks like there is an awk CSV parsing library here: http://lorance.freeshell.org/csv/ (I have not tried it).
Pitfall: validate an IPv4 addressLet’s say we want to check whether a given string is a valid IPv4 address (for simplicity, we limit our discussion to IPv4 addresses in the traditiona dotted quad format here). We start with this seemingly valid program:
awk -F ‘[.]‘ ‘function ok(n){return (n>=0 && n<=255)} {exit (ok($1) && ok($2) && ok($3) && ok($4))}’
This seems to work, until we pass it ’123b.44.22c.3′, which it happily accepts as valid. The fact is that, due to the way awk’s number to string conversion works, some strings may "look like" numbers to awk, even if we know they are not. The correct thing to do here is to perform a string comparison against a regular expression:
awk -F ‘[.]‘ ‘function ok(n) {
return (n ~ /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$/)
}
{exit (ok($1) && ok($2) && ok($3) && ok($4))}’Check whether two files contain the same data
We want to check whether two (unsorted) files contain the same data, that is, the set of lines of the first file is the same set of lines of the second file. One way is of course sorting the two files and processing them with some other tool (for example, uniq or diff). But we want to avoid the relatively expensive sort operation. Can awk help us here? The answer (you guessed it) is yes. If we know that the two files do not contain duplicates, we can do this:
awk ‘!($0 in a) {c++;a[$0]} END {exit(c==NR/2?0:1)}’ file1 file2
and check the return status of the command (0 if the files are equal, 1 otherwise). The assumption we made that the two files must not contain duplicate lines is crucial for the program to work correctly. In essence, what it does is to keep track of the number of different lines seen. If this number is exactly equal to half the number of total input records seen, then the two files must be equal (in the sense described above). To understand that, just realize that, in all other cases (ie, when a file is only a partial subset or is not a subset of the other), the total number of distinct lines seen will always be greater than NR/2.
The program’s complexity is linear in the number of input records.
Pitfall: contexts and variable types in awkWe have this file:
1,2,3,,5,foo
1,2,3,0,5,bar
1,2,3,4,5,bazand we want to replace the last field with "X" only when the fourth field is not empty. We thus do this:
awk -F ‘,’ -v OFS=’,’ ‘{if ($4) $6="X"}1′
But we see that the substitution only happens in the last line, instead of the last two as we expected. Why?
Basically, there are only two data types in awk: strings and numbers. Internally, awk does not assign a fixed type to the variables; they are literally considered to be of type "number" and "string" at the same time, with the number 0 and the null string being equivalent. Only when a variable is used in the program, awk automatically converts it to the type it deems appropriate for the context. Some contexts strictly require a specific type; in that case, awk automatically converts the variable to that type and uses it. In contexts that does not require a specific type, awk treats variables that "look like" numbers as numbers, and the other variables are treated as strings. In out example above, the simple test "if ($4)" does not provide a specific context, since the tested variable can be anything. In the first line, $4 is an empty string, so awk considers it false for the purposes of the test. In the second line, $4 is "0″. Since it look like a number, awk uses it like a number, ie zero. Since 0 is considered false, the test is unsuccessful and the substitution is not performed.
Luckily, there is a way to help awk and tell it exactly what we want. We can use string concatenation and append an empty string to the variable (which does not change its value) to explicitly tell awk that we want it to treat it like a string, or, conversely, add 0 to the variable (again, without changing its value) to explicitly tell awk that we want a number. So this is how our program should be to work correctly:
awk -F ‘,’ -v OFS=’,’ ‘{if ($4″") $6="X"}1′ # the "" forces awk to evaluate the variable as a string
With this change, in the second line the if sees the string "0″, which is not considered false, and the test succeeds, just as we wanted.
As said above, the reverse is also true. Another typical problematic program is this:
awk ‘/foo/{tot++} END{print tot}’
This, in the author’s intention, should count the number of lines that match /foo/. But if /foo/ does not appear in the input, the variable tot retains its default initial value (awk initializes all variables with the dual value "" and 0). print expects a string argument, so awk supplies the value "". The result is that the program prints just an empty line. But we can force awk to treat the variable as numeric, by doing this:
awk ‘/foo/{tot++} END{print tot+0}’
The seemingly innocuous +0 has the effect of providing numeric context to the variable "tot", so awk knows it has to prefer the value 0 of the variable over the other possible internal value (the empty string). Then, numeric-to-string conversion still happens to satisfy print, but this time what awk converts to string is 0, so print sees the string "0″ as argument, and prints it.
Note that, if an explicit context has been provided to a variable, awk remembers that. That can lead to unexpected results:
# input: 2.5943 10
awk ‘{$1=sprintf("%d",$1); # truncates decimals, but also explicitly turns $1 into a string!
if($1 > $2) print "something went wrong!" } # this is printedHere, after the sprintf(), awk notes that we want $1 to be a string (in this case, "2″). Then, when we do if($1>$2), awk sees that $2 has no preferred type, while $1 does, so it converts $2 into a string (to match the wanted type of $1) and does a string comparison. Of course, 99.9999% of the times this is not what we want here. In this case, the problem is easily solved by doing "if ($1+0 > $2)" (doing $2+0 instead WON’T work!), doing "$1=$1+0″ after the sprintf(), or using some other means to truncate the value of $1, that does not give it explicit string type.
Pulling out thingsSuppose you have a file like this:
Yesterday I was walking in =the street=, when I saw =a
black dog=. There was also =a cat= hidden around there. =The sun= was shining, and =the sky= was blue.
I entered =the
music
shop= and I bought two CDs. Then I went to =the cinema= and watched =a very nice movie=.
End of the story.Ok, silly example, fair enough. But suppose that we want to print only and all the parts of that file that are like =something=. We have no knowledge of the structure of the file. The parts we’re interested in might be anywere; they may span lines, or there can be many of them on a single line. This seemingly daunting and difficult task is actually easily accomplished with this small awk program:
awk -v RS=’=’ ‘!(NR%2)’
# awk -v RS=’=’ ‘!(NR%2){gsub(/\n/," ");print}’ # if you want to reformat embedded newlinesEasy, wasn’t it? Let’s see how this works. Setting RS to ‘=’ tells awk that records are separated by ‘=’ (instead of the default newline character). If we look at the file as a series of records separated by ‘=’, it becomes clear that what we want are the even-numbered records. So, just throw in a condition that is true for even-numbered records to trigger the printing.
GNU awk can take this technique a step further, since it allows us to assign full regexes to RS, and introduces a companion variable (RT) that stores the part of the input that actually matched the regex in RS. This allows us, for example, to apply the previous technique when the interesting parts of the input are delimited by different characters or string, like for example when we want everything that matches <tag>something</tag>. With GNU awk, we can do this:
gawk -v RS=’</?tag>’ ‘RT=="</tag>"‘
or again
gawk -v RS=’</?tag>’ ‘!(NR%2)’and be done with that. Another nice thing that can be done with GNU awk and RT is printing all the parts of a file that match an arbitrary regular expression (something otherwise usually not easily accomplished). Suppose that we want to print everything that looks like a number in a file (simplifiying, here any sequence of digits is considered a number, but of course this can be refined), we can do just this:
gawk -v RS=’[0-9]+’ ‘RT{print RT}’Checking that RT is not null is necessary because for the last record of a file RT is null, and an empty line would be printed in that case. The output produced by the previous program is similar to what can be obtained using grep -o. But awk can do better than that. We can use a slight variation of this same technique if we want to add context to our search (something grep -o alone cannot do). For example, let’s say that we want to print all numbers, but only if they appear inside "--", eg like --1234--, and not otherwise. With gawk, we can do this:
gawk -v RS=’--[0-9]+--’ ‘RT{gsub(/--/,"",RT);print RT}’
So, a carefully crafted RS selects only the "right" data, that can be subsequently extracted safely and printed.
———————————————————————-
SUPPRIMER LES LETTRESecho "abc654dEF" | tr -d ‘a-zA-Z’→ 654———
zts Permalink
(I’ll try that again) Alternate solution for the IP address validation function - same as your first suggestion, but with a condition allowing only numeric values:function ok(n){ return (n !~ /[^0-9]/) && (n>=0 && n
———
To remove all double quotes and angled brackets, try this:gsub(/["<>]/,"")To replace all semicolons and colons with newlines, try this:
gsub(/[;:]/,"\n")
———
Marco R. PermalinkIn "Pitfall: validate an IPv4 address" awk returns not-zero when the input is a valid IPv4 address and zero otherwise. That’s because awk’s boolean arithmetic assigns 1 to True and 0 to False.
This is not what a shell programmer would expect because shells usually act in the opposite way: true=0 and false=1.
Thus, the final "shell-compatible" script should be:
awk -F ‘[.]‘ ‘function ok(n) {
return (n ~ /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$/)
}
{exit (! ( ok($1) && ok($2) && ok($3) && ok($4) ) )}’However, I’d prefer to use something simpler:
function ok(n) {if (n ~ /[^[:digit:]]/) return 1==0;return (nNot fully tested but should work the samemy 2 pennies ;)
———
waldner Permalink@Marco R:
Awk’s logic is not broken. I just follows the C (and many other languages) model, where any nonzero value is considered true, so you can do
"if <something> then …".The shell uses the same logic, but it just uses zero to indicate success and nonzero to indicate failure, but that’s not necessarily a bad thing.
In the particular application of validating an address, the return value depends on the context you’ll use that. If the function ok() is to be invoked within the awk program only, then I think returning 1 for success makes sense (and perhaps return 0 at the end of the overall awk program if it succeeded in its entirety). If, on the other hand, you want to use the awk program directly from the shell, only to check IP addresses, then you can return 0 for success, as you suggest. I think it’s a matter of personal taste after all.
Regarding the function ok() you suggest, something similar was proposed by another reader (see previous comments). However, I don’t see the point in returning "1==0″ instead of simply 0.
———
karl PermalinkIt’s easy with sed:
echo abc654def | sed -E -n -e ‘s/abc([0-9]+)def/\1/p’
———
Rob PermalinkCould I ask something basic? If I have an IP=123.123.123.123 how do I use the ‘Pitfall: validate an IPv4 address’ to validate the variable/input?
———
waldner Permalink@Rob:
many solutions have been proposed in the article and in the comments. To summarize, you can build a function that accepts an argument and checks if that argument is a number and is between 0 and 255, and use that function to check that all four octets are valid. The skeleton of a program is as follows:awk -F[.] ‘
function ok(n) {
# do something here to check the value of n,
# and return 1 if it’s valid, 0 otherwise
}
{exit ( ok($1) && ok($2) && ok($3) && ok($4) )’(to make it more robust, you may optionally check that the number of input fields (ie, octets) is exactly 4, no more and no less)
so you will call it as follows, for example:echo "123.123.123.123″ | awk -F[.] ‘…’
if [ $? -eq 1 ]; then
echo "valid IP address"
else
echo "invalid IP address"
fiwhere the part ‘…’ is the complete awk program.
Note that, to follow shell conventions, you may choose to have the awk program return 0 on success and 1 on failure instead.That said, here are two ways to implement function ok:
function ok(n) {
# string check using a regex
return (n~/^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$/)
}function ok(n) {
# check that n is a number, and then that
# it is in the range 0-255
return (n~/^[0-9]*$/ && n>=0 && n<=255)
}Again, you might choose to negate the return values if you want to return 0 on success.
effacer the duplicates.
awk ‘!a[$0]++’search a file for a pattern ($3 in input line) and for a match print $1 and $2 and $3, but for no match print "na " "na " $3 :
$ awk ‘$3 ~ /pattern/ { print $1, $2, $3 } $3 !~ { print "na", "na", $3 }’
or
$ awk ‘$3 ~ /pattern/ { print $1, $2, $3; next } { print "na", "na", $3 }’
or
$ awk ‘{ if ($3 ~ /pattern/) { print $1, $2, $3 } else { print "na", "na", $3 } }’awk ‘{gsub(/pattern/,"foobar")}1′is the equivalent tosed ‘s/pattern/foobar/g’awk ‘/foo/ || /bar/’more simpleawk ‘/foo|bar/’This will work:
awk -v lvar="${VAR}" ‘match($0, lvar) { print $0 }’ myfile———————————————————————————-
ESPACEMENT DE FICHIER:
# espacer en double un fichier sed G # espacer en double un fichier qui a déjà des lignes vides. # Le fichier de sortie n'aura jamais plus qu'une ligne vide # entre les lignes de texte. sed 'G;G' # défaire le double-espacement (assumons les lignes paires comme # étant toujours vides) sed 'n;d' # espacer en triple un fichier sed 'G;G' # insérer une ligne vide au-dessus de chaque ligne qui contient "regex" sed '/regex/{x;p;x;}' # insérer une ligne vide sous chaque ligne qui contient l'expression # régulière "regex" sed '/regex/G' # insérer une ligne vide au-dessus et au-dessous de chaque ligne qui # contient "regex" sed '/regex/{x;p;x;G;}' NUMÉROTATION: # numéroter chaque ligne du fichier (appuyé simplement à gauche). # L'utilisation d'une tabulation (voir la note sur '\t' à la fin # de ce texte) au lieu d'un espace préservera les marges. sed = nomdefichier | sed 'N;s/\n/\t/' # numéroter chaque ligne d'un fichier (numéro à gauche, appuyé à droite) sed = nomdefichier | sed 'N; s/^/ /; s/ *\(.\{6,\}\)\n/\1 /' # numéroter chaque ligne d'un fichier, mais afficher le numéro de ligne # seulement si la ligne n'est pas vide. sed '/./=' nomdefichier | sed '/./N; s/\n/ /' # compter les lignes (émulation de "wc -l") sed -n '$=' CONVERSION de TEXTE et SUBSTITUTION: # ENVIRONEMENT UNIX: conversion des retour de chariot (CR/LF) au format Unix. sed 's/.$//' # assume que toutes les lignes se terminent avec CR/LF sed 's/^M$//' # sous bash/tcsh, enfoncer Ctrl-V puis Ctrl-M sed 's/\x0D$//' # fonctionne sous ssed, gsed 3.02.80 ou plus récent # ENVIRONEMENT UNIX: conversion des retour de chariot UNIX (LF) au format DOS. sed "s/$/`echo -e \\\r`/" # ligne de commande sous ksh sed 's/$'"/`echo \\\r`/" # ligne de commande sous bash sed "s/$/`echo \\\r`/" # ligne de commande sous zsh sed 's/$/\r/' # gsed 3.02.80 ou plus haut # ENVIRONMENT DOS: convertir les retour de chariot Unix (LF) au format DOS. sed "s/$//" # méthode 1 sed -n p # méthode 2 # ENVIRONMENT DOS: convertir les retour de chariot DOS (CR/LF) au format Unix. # Peut seulement être utilisé avec UnxUtils sed, version 4.0.7 ou plus récente. # La version UnxUtils peut être utilisée avec le modificateur "--text" # qui apparaît lorsque vous utiliser le modificateur "--help". Sinon, # la conversion des retours de chariot DOS vers la version UNIX ne peut # se faire avec SED dans un environnement DOS. Utiliser 'tr' au lieu. sed "s/\r//" infile >outfile # UnxUtils sed v4.0.7 ou plus récent tr -d \r <infile >outfile # GNU tr version 1.22 ou plus récent # éliminer tout espace blanc (espaces, tabulations) à la gauche # de chaque ligne, et appuyer le résultat à la marge gauche sed 's/^[ \t]*//' # voir note au sujet de '\t' à la fin de ce fichier # éliminer tout espace blanc (espaces, tabulations) à la fin de chaque ligne sed 's/[ \t]*$//' # voir note au sujet de '\t' à la fin de ce fichier # éliminer tout espace blanc des deux bouts de chaque ligne sed 's/^[ \t]*//;s/[ \t]*$//' # insérer 5 espaces au début de chaque ligne (décalage de page vers la droite) sed 's/^/ /' # aligner tout le texte à la droite sur la 79e colonne sed -e :a -e 's/^.\{1,78\}$/ &/;ta' # mettre à 78 plus un espace # centrer tout le texte sur le centre de la 79e colonne. Dans la première # méthode, tout espace au début de la ligne est significatif, et # des espaces sont ajoutés à la fin de la ligne. Dans la deuxième # méthode, les espaces précédant les lignes sont ignorés pendant # le processus de centrage, et aucun espace n'est ajouté à la fin des lignes. sed -e :a -e 's/^.\{1,77\}$/ & /;ta' # méthode 1 sed -e :a -e 's/^.\{1,77\}$/ &/;ta' -e 's/\( *\)\1/\1/' # méthode 2 # substituer (trouver et remplacer) "foo" avec "bar" sur chaque ligne sed 's/foo/bar/' # replacer seulement la première instance de la ligne sed 's/foo/bar/4' # replacer seulement la quatrième instance de la ligne sed 's/foo/bar/g' # replacer toutes les instances de la ligne sed 's/\(.*\)foo\(.*foo\)/\1bar\2/' # replacer l'avant-dernier cas sed 's/\(.*\)foo/\1bar/' # replacer seulement le dernier cas # substituer "foo" par "bar" SEULEMENT pour les lignes contenant "baz" sed '/baz/s/foo/bar/g' # substituer "foo" par "bar" A L'EXCEPTION des lignes contenant "baz" sed '/baz/!s/foo/bar/g' # substituer "scarlet" ou "ruby" ou "puce" par "red" sed 's/scarlet/red/g;s/ruby/red/g;s/puce/red/g' # la plupart des seds gsed 's/scarlet\|ruby\|puce/red/g' # GNU sed seulement # reverser l'ordre des lignes (émulation de "tac") # bug/boni dans HHsed v1.5 cause l'élimination des lignes vides sed '1!G;h;$!d' # méthode 1 sed -n '1!G;h;$p' # méthode 2 # renverse l'ordre de chaque caractère sur une ligne (émulation de "rev") sed '/\n/!G;s/\(.\)\(.*\n\)/&\2\1/;//D;s/.//' # joindre des paires de lignes ensemble côte-à-côte (émulation de "paste") sed '$!N;s/\n/ /' # si une ligne se termine par une barre oblique inversée, # joindre la ligne suivante à la présente sed -e :a -e '/\\$/N; s/\\\n//; ta' # si une ligne débute par le symbole égalité, l'ajouter à la précédente # et remplacer le symbole "=" par un espace simple sed -e :a -e '$!N;s/\n=/ /;ta' -e 'P;D' # insérer des virgules aux chaînes numériques, changeant "1234567" en # "1,234,567" gsed ':a;s/\B[0-9]\{3\}\>/,&/;ta' # GNU sed sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/;ta' # autres seds # décimaux et signes négatifs (GNU sed) gsed -r ':a;s/(^|[^0-9.])([0-9]+)([0-9]{3})/\1\2,\3/g;ta' # ajouter une ligne blanche à chaque cinq lignes (après lignes 5, 10, 15, 20, etc.) gsed '0~5G' # GNU sed seulement sed 'n;n;n;n;G;' # autres seds IMPRIMER D'UNE FACON SÉLECTIVES CERTAINES LIGNES: # imprimer les dix premières lignes d'un fichier (émulation de "head") sed 10q # imprimer la première ligne d'un fichier (émulation "head -1") sed q # imprimer les dernières dix lignes d'un fichier (émulation "tail") sed -e :a -e '$q;N;11,$D;ba' # imprimer les dernières deux lignes d'un fichier (émulation "tail -2") sed '$!N;$!D' # imprimer la dernière ligne d'un fichier (émulation "tail -1") sed '$!d' # méthode 1 sed -n '$p' # méthode 2 # imprimer l'avant-dernière ligne d'un fichier sed -e '$!{h;d;}' -e x # pour fichiers d'une ligne , imprimer une ligne vide sed -e '1{$q;}' -e '$!{h;d;}' -e x # pour fichiers d'une ligne , imprimer la ligne sed -e '1{$d;}' -e '$!{h;d;}' -e x # pour fichiers d'une ligne , ne rien imprimer # imprimer seulement les lignes coïncidant avec l'expression régulière regexp # (émulation "grep") sed -n '/regexp/p' # méthode 1 sed '/regexp/!d' # méthode 2 # imprimer seulement les lignes NE coïncidant PAS avec l'expression régulière regexp # (émulation "grep -v") sed -n '/regexp/!p' # méthode 1, corresponds avec méthode ci-haut sed '/regexp/d' # méthode 2, syntaxe plus simple # imprimer la ligne précédant celle qui coïncide avec regexp, # mais pas la ligne coïncidant avec regexp sed -n '/regexp/{g;1!p;};h' # imprime la ligne suivant celle qui coïncide avec regexp, # mais pas la ligne coïncidant avec regexp sed -n '/regexp/{n;p;}' # imprime une ligne de contexte avant et après regexp, avec numérotation # de lignes indiquant l'endroit de coïncidence avec regexp (similaire # à "grep -A1 -B1") sed -n -e '/regexp/{=;x;1!p;g;$!N;p;D;}' -e h # grep pour AAA et BBB et CCC (peu importe l'ordre) sed '/AAA/!d; /BBB/!d; /CCC/!d' # grep pour AAA et BBB et CCC (dans cet ordre) sed '/AAA.*BBB.*CCC/!d' # grep pour AAA ou BBB ou CCC (émulation "egrep") sed -e '/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d # la plupart des seds gsed '/AAA\|BBB\|CCC/!d' # GNU sed seulement # imprime paragraphe si il contient AAA (paragraphes séparés par des lignes vides) # HHsed v1.5 veuillez insérer un 'G;' après 'x;' dans les trois commandes ci-bas sed -e '/./{H;$!d;}' -e 'x;/AAA/!d;' # imprime le paragraphe s'il contient AAA et BBB et CCC (peu importe l'ordre) sed -e '/./{H;$!d;}' -e 'x;/AAA/!d;/BBB/!d;/CCC/!d' # imprime le paragraphe s'il contient AAA ou BBB ou CCC sed -e '/./{H;$!d;}' -e 'x;/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d gsed '/./{H;$!d;};x;/AAA\|BBB\|CCC/b;d' # GNU sed seulement # imprime seulement les lignes longues de 65 caractères ou plus sed -n '/^.\{65\}/p' # imprime seulement les lignes longues de moins de 65 caractères sed -n '/^.\{65\}/!p' # méthode 1, correspond à ci-haut sed '/^.\{65\}/d' # méthode 2, syntaxe plus simple # imprime la partie du fichier depuis la coïncidence à l'expression # régulière, jusqu'à la fin du fichier sed -n '/regexp/,$p' # imprime la partie du fichier incluse par le numéros de ligne 8-12 inclusivement sed -n '8,12p' # méthode 1 sed '8,12!d' # méthode 2 # imprime la ligne numéro 52 sed -n '52p' # méthode 1 sed '52!d' # méthode 2 sed '52q;d' # méthode 3, très efficace sur de gros fichiers # commençant avec la troisième ligne, imprimer chaque septième ligne gsed -n '3~7p' # GNU sed seulement sed -n '3,${p;n;n;n;n;n;n;}' # autres seds # imprime la partie du fichier contenue entre deux expression régulières # incluant celles contenant les expressions régulières sed -n '/Iowa/,/Montana/p' # respecte les hauts de casse ÉCRASEMENT SÉLECTIF DE CERTAINES LIGNES: # imprime tout le fichier SAUF la section coïncidant avec deux # expressions régulières sed '/Iowa/,/Montana/d' # élimine les lignes consécutives identiques d'un fichier (émulation "uniq"). # La première ligne d'un ensemble de lignes identiques consécutives # est retenue, les autres éliminées sed '$!N; /^\(.*\)\n\1$/!P; D' # élimine les lignes en duplicata, non-consécutives, d'un fichier. # Prenez garde de ne pas déborder les limites de la mémoire tampon # sinon veuillez utiliser le GNU sed. sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P' # éliminer toutes les linges sauf celles en duplicata (émulation "uniq -d"). sed '$!N; s/^\(.*\)\n\1$/\1/; t; D' # éliminer les dix première lignes d'un fichier sed '1,10d' # écraser la dernière ligne d'un fichier sed '$d' # écraser les deux dernières lignes d'un fichier sed 'N;$!P;$!D;$d' # écraser les dix dernières lignes d'un fichier sed -e :a -e '$d;N;2,10ba' -e 'P;D' # méthode 1 sed -n -e :a -e '1,10!{P;N;D;};N;ba' # méthode 2 # écraser chaque huitième ligne gsed '0~8d' # GNU sed seulement sed 'n;n;n;n;n;n;n;d;' # autres seds # écraser les lignes coïncidant avec un patron sed '/patron/d' # écraser TOUTES les lignes vides d'un fichier (émulation "grep '.' ") sed '/^$/d' # méthode 1 sed '/./!d' # méthode 2 # écraser toutes les lignes vides consécutives (sauf la première); # aussi écrase toutes les lignes vides du début et de la fin d'un # fichier (émulation "cat -s") sed '/./,/^$/!d' # méthode 1, retient 0 ligne vide au début, 1 à la fin sed '/^$/N;/\n$/D' # méthode 2, permet 1 ligne vide au début, 0 à la fin # écraser toutes lignes vides CONSÉCUTIVES d'un fichier, sauf les deux premières: sed '/^$/N;/\n$/N;//D' # écraser toutes les lignes vides au début d'un fichier sed '/./,$!d' # écraser toutes les lignes vides de la fin d'un fichier sed -e :a -e '/^\n*$/{$d;N;ba' -e '}' # fonctionne sur tous les seds sed -e :a -e '/^\n*$/N;/\n$/ba' # ibid, sauf pour gsed 3.02.* # écrase la dernière ligne de chaque paragraphe sed -n '/^$/{p;h;};/./{x;/./p;}' APPLICATIONS SPÉCIALES: # éliminer les sur-frappes nerf (char, retour) des pages man. La commande # 'echo' peut nécessiter le modificateur -e si vous utilisez Unix System V # ou du shell bash. sed "s/.`echo \\\b`//g" # double guillemets requis dans l'environnement Unix sed 's/.^H//g' # sous bash/tcsh, enfoncer Ctrl-V et ensuite Ctrl-H sed 's/.\x08//g' # expression hexadécimale pour sed 1.5, GNU sed, ssed # obtenir l'entête des messages Usenet/courriel sed '/^$/q' # élimine tout ce qui suit la première ligne vide # obtenir le corps des messages Usenet/courriel sed '1,/^$/d' # élimine tout ce qui précède la première ligne vide # obtenir l'entête Sujet, mais élimine la portion initiale "Subject: " sed '/^Suject: */!d; s///;q' # obtenir l'adresse de retour dans l'entête sed '/^Reply-To:/q; /^From:/h; /./d;g;q' # parcourir et isoler l'adresse proprement dite. Extirpe l'adresse courriel # par elle-même, du script précédent sed 's/ *(.*)//; s/>.*//; s/.*[:<] *//' # ajouter un crochet et espace à chaque ligne (citer un message) sed 's/^/> /' # écraser le crochet et espace précédant chaque ligne (enlever la citation du message) sed 's/^> //' # écraser la plupart des étiquettes HTML (s'accommode des étiquettes multi-lignes) sed -e :a -e 's/<[^>]*>//g;/</N;//ba' # extrait les parties uuencodées binaires, éliminant les entêtes # superflues, de façon à garder seulement la partie uuencodée. Les # fichiers doivent être passé à sed dans le bon ordre. La version 1 # peut être passée depuis la ligne de commande; la version 2 peut # faire partie d'un script de shell Unix. (Modifiée par un script # originaire de Rahul Dhesi.) sed '/^end/,/^begin/d' file1 file2 ... fileX | uudecode # vers. 1 sed '/^end/,/^begin/d' "$@" | uudecode # vers. 2 # triage des paragraphes d'un fichier par ordre alphabétique. Les paragraphes # sont séparés pour des lignes vides. GNU sed utilise \v comme tabulation # verticale, ou n'importe lequel caractère unique peut servir. sed '/./{H;d;};x;s/\n/={NL}=/g' file | sort | sed '1s/={NL}=//;s/={NL}=/\n/g' gsed '/./{H;d};x;y/\n/\v/' file | sort | sed '1s/\v//;y/\v/\n/' # compresser en zip chaque fichier .TXT individuellement, écrasant # le fichier source et assignant le nom du fichier compressé .ZIP # au nom de base du fichier source .TXT (sous DOS: le modificateur # "dir /b" retourne les noms de base des fichiers tout en majuscules) echo @echo off >zipup.bat dir /b *.txt | sed "s/^\(.*\)\.TXT/pkzip -mo \1 \1.TXT/" >>zipup.bat USAGE TYPIQUE: Sed accepte une ou plusieurs commandes et les applique toutes, de façon séquentielle, à chacune des lignes d'entrée. Une fois que toutes les commandes furent exécutées à la première ligne d'entrée, cette ligne est envoyée vers la sortie, une deuxième ligne est lue comme nouvelle entrée, et le cycle se répète. Les exemples précédents assument que l'entrée provient de l'entrée standard (ex. la console, normalement ce serait l'entrée dans un pipeline). Un ou plusieurs noms de fichiers peuvent être ajoutés à la ligne de commande si l'entrée ne provient pas de l'entrée standard. La sortie est passée à la sortie standard (stdout ou l'écran-témoin). Donc: cat nomdefichier | sed '10q' # utilise entrée pipeline sed '10q' nomdefichier # même chose, en moins du cat inutile sed '10q' nomdefichier > nouveaufichier # re dirige la sortie vers le disque Pour des renseignements additionnels sur la syntaxe, incluant comment fournir les instructions sed à partir d'un fichier au lieu de la ligne de commande, veuillez consulter le livre "SED & AWK, 2nd Edition," par Dale Dougherty et Arnold Robbins (O'Reilly, 1997; http://www.ora.com), "UNIX Text Processing," par Dale Dougherty and Tim O'Reilly (Hayden Books, 1987) ou les tutoriels par Mike Arst distribués dans U-SEDIT2.ZIP (plusieurs sites). Afin d'exploiter la pleine puissance de sed, l'usager doit comprendre les 'expressions régulières'. A cette fin, consultez "Mastering Regular Expressions" par Jeffrey Friedl (O'Reilly, 1997). Le manuel UNIX ("man") contient des pages qui pourraient être utiles ("man sed", "man regexp", ou la sous-section sur les expressions régulières ("man ed"), quoique les pages man sont notoires pour leur difficultés. Elles ne furent pas rédigées pour enseigner l'usage de sed ou des expressions régulières, mais comme texte de référence pour ceux qui connaissent déjà ces outils. SYNTAXE DE CITATION: Les exemples précédents utilisent les guillemets simples ('...') au lieu des guillemets doubles ("...") pour encadrer ou citer les commandes d'édition, puisque sed est typiquement utilisé sur les systèmes d'exploitation UNIX. Les guillemets simples préviennent le shell UNIX d'interpréter les symbole dollar ($) ainsi que les guillemets renversés (`...`) qui seraient interprétés par le shell s'ils seraient encadrés ou cités par les guillemets doubles. Les usagers du shell "csh" et dérivés doivent aussi citer le point d'exclamation avec l'oblique arrière (\!) si l'on veut que les exemples ci-haut fonctionnent, même avec à l'intérieur de guillemets simples. Les versions de sed écrites pour DOS invariablement requièrent des guillemets doubles ("...") au lieu des guillemets simples utilisés pour citer les commandes d'édition sed. L'USAGE DE '\t' DANS LES SCRIPTS SED: Afin de clarifier la documentation, nous avons utilisé l'expression '\t' pour indiquer le caractère de tabulation (0x09) dans les scripts. Cependant, la plupart des versions de sed ne reconnaissent pas l'abréviation '\t', donc, lorsque vous écrirez ces directives à l'invite de commande, vous devrez enfoncer la clef TAB au lieu de l'abréviation. '\t' est supporté comme métacharactère d'expression régulière dans awk, perl, et HHsed, sedmod, et GNU sed v3.02.80. VERSIONS DE SED: Les version de sed diffèrent entre elles, et de légers écarts de syntaxe se présentent. En particulier, la plupart ne reconnaissent pas l'usage d'étiquettes (:nom) ou ne permettent pas les instructions de branchement (b,t) à l'intérieur des commandes d'édition, sauf à la fin de ces commandes. Nous avons donc utilisé une syntaxe qui serait portable à la majorité des usagers de divers sed, bien que les versions les plus populaires du GNU sed permettent une syntaxe plus élaborée. Lorsque les lecteurs verront une longue chaîne de commande telle celle-ci: sed -e '/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d ils seront réjouis de savoir que GNU sed leur permettra de réduire le tout en ceci: sed '/AAA/b;/BBB/b;/CCC/b;d' # ou bien encore en ceci: sed '/AAA\|BBB\|CCC/b;d' En plus, se rappeler que bien que certaines versions de sed acceptent une commande telle "/one/ s/RE1/RE2/", d'autres ne permettent pas ceci: "/one/! s/RE1/RE2/", qui contient un espace avant le 's'. Éviter d'enter l'espace lorsque vous entrez la commande. OPTIMISATION POUR VITESSE D'EXÉCUTION: Lorsque vous avez besoin de vitesse pour l'exécution de vos scripts (si vos fichiers d'entrée sont volumineux, ou un processeur lent ou un disque dur lent) la substitution sera plus rapide si vous faites un recherche pour la chaîne à être changée avant de faire la substitution "s/.../.../". Voir: sed 's/foo/bar/g' nomdefichier # commande normale de substitution sed '/foo/ s/foo/bar/g' nomdefichier # exécution plus rapide sed '/foo/ s//bar/g' nomdefichier # raccourci de syntaxe Si vous devez altérer ou écraser seulement une section d'un fichier et que vous voulez seulement une sortie pour une première partie d'un fichier quelconque, la commande "quit" (q) dans le script réduira considérablement le temps d'exécution pour de gros fichiers. Donc: sed -n '45,50p' nomdefichier # imprimez les lignes nos. 45-50 d'un fichier sed -n '51q;45,50p' nomdefichier # ibid, mais bien plus vite
————————————————————————-
VOIR AUSSI :
https://buzut.fr/la-commande-sed-pour-les-nazes/
https://askubuntu.com/questions/76808/how-do-i-use-variables-in-a-sed-command
https://stackoverflow.com/questions/23826655/print-new-line-with-sed-using-variable
http://www.thegeekstuff.com/2009/11/unix-sed-tutorial-multi-line-file-operation-with-6-practical-examples/
https://stackoverflow.com/questions/6255796/how-the-n-command-works-in-sed
https://stackoverflow.com/questions/9434831/how-to-store-the-result-of-sed-in-variable-with-the-new-line-characters
http://www.commentcamarche.net/faq/9536-sed-introduction-a-sed-part-i