Voici en quelques exemples, quelques indices de l'incroyable efficacité de la lessive Sed couplée au détergent Awk.

Sed

Remplacer toutes les occurences du mot “tache” par le mot “blancheur” dans le fichier mon_linge, on redirige le tout vers le fichier mon_linge_propre :

$ sed s/tache/blancheur/g mon_linge > mon_linge_propre

Remplacer tous les debuts de ligne par un “.” dans le fichier mon_linge, on redirige le tout vers mon_linge_bien_range :

$ sed s/^/./ mon_linge > mon_linge_bien_range

Remplacer tous les fins de ligne par un “.” dans le fichier mon_linge, on redirige le tout vers mon_linge_bien_range :

$ sed s/$/./ mon_linge > mon_linge_bien_range

Effacer toutes les lignes contenant le mot “tache” dans le fichier mon_linge, on redirige le tout vers le fichier mon_linge_propre :

$ sed /tache/d mon_linge > mon_linge_propre

Effacer toutes les lignes contenant le mot “tache” et remplacer tous les fins de ligne par un “.”, on redirige vers mon_linge_qui_sent_bon :

$ sed -e 's/$/./' -e  's/.*tache.*/\\0/' > mon_linge_qui_sent_bon

Awk

Afficher la 2eme colonne ( PID ) du resultat d'un ps aux :

$ ps aux|awk '{print $2}'

Afficher la 2eme colonne ( PID ) du resultat d'un ps aux en le faisant précéder d'un label :

$ ps aux|awk '{printf("PID: %s\n"), $2}'

Afficher la 2eme colonne ( PID ) du resultat d'un ps aux en le faisant précéderd'un label et en affichant l'utilisateur :

$ ps aux|awk '{printf("PID: %s utilise par %s\n"), $2, $1}'

Afficher l'/etc/passwd bien rangé :

$ awk -F: '{printf("Utilisateur: %s\nUID: %s\nGID: %s\nNom: %s\nHome: %s\nShell: %s\n", $1, $3, $4, $5, $6, $7)}' /etc/passwd

Afficher les users dont le shell est /bin/bash

awk -F: '$7=="/bin/bash" {print $1}' /etc/passwd

Utiliser les fonctions mathématiques :

$ echo "3 4" | awk '{print int($1+$2)}'

(affiche normalement 7, si tout se passe bien)

Utiliser les fonctions mathématiques (2):

ls -1sk /usr/bin | sort -n | awk '$1 > 500'

(affiche le nom des executables de /usr/bin dont la taille est supérieure a 500 k)

Sed + Awk

Le but de la manip était de passer de proftpd “classic” à proftpd-mysql+proftpd administrator (http://proftpd-adm.sourceforge.net/)

En entrée, un fichier de type passwd contenant username, passwd, uid, gid, etc..

$ cat /etc/proftpd.passwd 
64telecom:$1$YVOCtxyK$jQEyhesyMwECPgHP0Yh1O0:111:65534::/home/commun/ftp-externe/64telecom:/bin/false
bgsa:$1$Dzy3LXzx$5z4OrKHQjxEINcg5XIrBl.:111:65534::/home/commun/ftp-externe/bgsa:/bin/false
mpsftp:$1$dNm0LqxW$E7t4TuNkv/srqp4HRL3Xz1:111:65534::/home/commun/ftp-externe/mpsftp:/bin/false
mpsftp1:$1$YTfhVzwS$iBUOELFxp.fT3tH7GjPd5.:111:65534::/home/commun/ftp-externe/mpsftp/mpsftp1:/bin/false
..
(150 lignes)

En sortie, des INSERT INTO en veux-tu, en voilà, afin d'insérer tout ça dans une base de données MySQL. Un exemple ce que je voulais:

INSERT INTO usertable (userid, passwd, homedir, shell, uid, gid, count, lastlogin, lastlogout, expiration, disabled, det_name, det_mail, det_adress, det_notes) VALUES ('mobisfraf83', '$1$WVPoRVPU$gWEuzxgS7ZZ2mN/BYVedd0', '/home/commun/ftp-externe/mobisfraf83', '/bin/false', '2154', '10001', '0', '0000-00-00 00:00:00', '0000-00-00 00:00:00', '0000-00-00 00:00:00', '0', 'mobisfraf83', 'mobisfraf83', 'mobisfraf83', 'mobisfraf83');

Sed et Awk, c'est un peu la fusion de 2 super Sayen dans Dragon Ball Z. J'en veux pour exemple mon code:

#!/bin/sh
a="2000"
for i in `cat /etc/proftpd.passwd`
do      
        echo $i \ | 
        cut -d":" -f 1,2,6 | \
        sed 's/\:/ /g' | \
        awk -v sq="'" '{print "INSERT INTO usertable (userid, passwd, homedir, shell, uid, gid, count, lastlogin, lastlogout, expiration, disabled, det_name, det_mail, det_adress, det_notes) VALUES ('"'"'" $1 "'"'"', " "'"'"'" $2 "'"'"', " "'"'"'" $3 "'"'",' " sq "/bin/false" sq ", " sq "111" sq ", " sq "10001" sq ", " sq "0" sq ", " sq "0000-00-00 00:00:00" sq ", " sq "0000-00-00 00:00:00" sq ", " sq "0000-00-00 00:00:00" sq ", " sq "0" sq ", '"'"'" $1 "'"'"', " "'"'"'" $1 "'"'"', " "'"'"'" $1 "'"'"', " "'"'"'" $1 "'"'"'" ");"}' | \
        sed s/111/$a/g
        a=$(expr $a + 1)
done

Explications: echo affiche sur la sortie les lignes du fichier 1 à 1. cut découpe et prend les colonnes 1, 2, 6 avec comme séparateur “:”. sed remplace les 2 points par une espace. awk fait son boulôt en affichant ce qui reste là où je lui demande de les afficher ($1, $2, $3). À noter: le garçon est TRES CHIANT en ce qui concerne le traitement des singles quotes (vraiment!). J'ai bidouillé un peu, mais ça fonctionne (il existe plusieurs manières de traiter les singles quotes, Google is your friend). sed termine la marche en remplaçant la chaine 111 par une variable que j'incrémente de 1 à chaque fois et qui commence à 2000 (l'UID des utilisateurs).

On sauvegarde et on execute. 150 requêtes en un claquement de doigt. Moi je dis, MESSIEURS..

Mais subtil rebondissement, l'admin etant une grosse moule il ne supporte pas la taille gigantesque de ce script et decide de l'ecrire un un seul statement de awk, evitant un nombre incalculable (enfin si, tres calculable) de generations de processus pour le reduire a un seul

awk -F: -v s="'" -v c=", " -v "a=2000" '{print "INSERT INTO usertable (userid, passwd, homedir, shell, uid, gid, count, lastlogin, lastlogout, expiration, disabled, det_name, det_mail, det_adress, det_notes) VAL
UES (" s $1 s c s $2 s c s $6 s c s "/bin/false" s c s a++ s c s "10001" s c s "0" s c s "0000-00-00 00:00:00" s c s "0000-00-00 00:00:00" s c s "0000-00-00 00:00:00" s c s "0" s c s $1 s c s $1 s c s $1 s c s $
1 s ");" }' /etc/proftpd.passwd

Des lors l'admin contemple son oeuvre et derriere son apparence de goret immonde se trouve une ame de poete, il lui faut utiliser les qualites intrinseques de awk et ne pas souiller son appel de vilains flags inutiles et reserves aux tacherons:

awk 'BEGIN { sep="\047, \047"; a=2000; FS=":" } {print "INSERT INTO usertable (userid, passwd, homedir, shell, uid, gid, count, lastlogin, lastlogout, expiration, disabled, det_name, det_mail, det_adress, det_notes) VALUES (\047" $1 sep $2 sep $6 sep "/bin/false" sep a++ sep "10001" sep "0" sep "0000-00-00 00:00:00" sep "0000-00-00 00:00:00" sep "0000-00-00 00:00:00" sep "0" sep $1 sep $1 sep $1 sep $1 "\047);" }' /etc/proftpd.passwd

Ici on utilise BEGIN qui est un pattern particulier utilise pour matcher avant la premiere ligne.

Notre admin, fort de sa maitrise nouvelle du puissant awk, decide qu'il a envie d'afficher les lignes de son fichier proftpd.passwd, mais a l'envers. Il se lance dans la reecriture du celebre tac:

awk     'END    { for (i = NR - 1; i >= 0; i--) { print lines[i]; }}
                { lines[i++] = $0 }'

Ici c'est le pattern particulier END qui est utilise.

Note: Sed et Awk peuvent également etre utilisés en cascade ( pipe: | ) pour encore plus de fraicheur.

unix/sed_et_awk.txt · Last modified: 2010/01/12 13:29 (external edit)