Le b-a BAsh du Shell — P
Transcription
Le b-a BAsh du Shell — P
Le b-a BAsh du Shell — P-F. Bonnefoi Version du 28 janvier 2016 Table des matières 1 NFA003 – P-F.B. 2 3 4 5 6 7 Les scripts Bash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La définition de variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La notion de chaîne de caractères & de substitution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les conditionnelles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les Redirections d’E/S . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les boucles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . L’exécution séquentielle ou en «multi-tâche» . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Entrer des valeurs dans un script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La commande grep . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Expressions régulières ou expressions rationnelles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La commande sed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La commande awk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Combinaison de shell et de la commande find . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Opérations avancées : décomposition nom fichier & descripteur de fichier . . . . . . . . . . . . . . . . . . Opérations avancées : les tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 4 5 6 7 8 10 12 14 15 17 18 19 21 25 27 28 1 Les scripts Bash 3 Création de script Un script est un fichier texte contenant une séquence d’instructions. Pour permettre à un script d’être exécuté directement depuis la ligne de commande, il est nécessaire de mettre une première ligne particulière (fichier helloworld) : 1 #!/bin/bash 2 echo "Hello, world!" # ceci est un commentaire compris entre le "#" et la fin de la ligne Cette première ligne indique au système que ce fichier texte contient un script qui peut être exécuté par l’interprète. xterm $ file helloworld test_bash: Bourne-Again shell script text executable Ensuite, sous la ligne de commande (en ayant sauvegardé le script sous le nom helloworld : xterm $ chmod +x helloworld $ ./helloworld Hello, world! NFA003 – P-F.B. Il est possible de lancer le script dans un nouveau shell ou dans le shell courant : xterm $ bash helloworld Hello, world! $ source helloworld Hello, world! La première exécution lance un «sous-shell» et la seconde lance dans le shell courant, ce qui peut être intéressant dans le cas de la définition de variable d’environnement : elles sont conservées. La définition de variable Il est possible de définir des variables : var1=1 var2=false var3="/home/$user/file.txt" Pour accéder à leur contenu, il faut les «préfixer» par «$» : xterm $ echo $var1 1 shell et sous-shell Soit le script, «exemple_de_script» : 1 #!/bin/bash 2 ma_variable=bonjour Et l’utilisation en sous-shell : xterm $ bash exemple_de_script $ echo $ma_variable $ NFA003 – P-F.B. Et en shell : xterm $ source exemple_de_script $ echo $ma_variable bonjour La variable et sa valeur ont été ajoutées aux variables d’environnement du shell courant. 4 La notion de chaîne de caractères & de substitution 5 L’utilisation des chaînes de caractères permet de regrouper en un seul élément différents mots séparés par des espaces, ce qui est nécessaire en cas d’ambigüité : xterm $ grep "le petit chaperon rouge" mon_fichier.txt xterm $ grep le petit chaperon rouge mon_fichier.txt grep le petit chaperon rouge mon_fichier.txt grep: petit: Aucun fichier ou dossier de ce type grep: chaperon: Aucun fichier ou dossier de ce type Substitution & Exécution Il est existe 3 formes de chaîne de caractères : ▷ les guillemets «"» : faire de la substitution du nom d’une variable préfixée par un «$» par son contenu : xterm $ ma_variable=toto $ echo "bonjour $ma_variable" bonjour toto ▷ les quotes «’» : bloque la substitution : xterm $ echo 'bonjour $ma_variable' bonjour $ma_variable ▷ les back-quotes «`» : permettent de substituer la chaîne par le NFA003 – P-F.B. résultat de la commande qu’elle contient xterm $ date_aujourdhui=`date` $ echo $date_aujourdhui Thu Jan 28 18:05:38 CET 2016 Ici, on cherche exactement le texte «le petit chaperon» dans le fichier mon_fichier.txt : Là, l’absence de «"» amène la commande grep à considérer le premier mot, «le», comme la chaîne à rechercher, puis les mots suivants comme des noms de fichier. Conseil 1 Pour améliorer la reconnaissance de l’identifiant de la variable, il est conseillé de l’entourer de «{}» "substitution de ${ma_chaine}" est différent de : "substitution de ${ma}_chaine" mais identique à : "substitution de $ma_chaine" Conseil 2 On peut utiliser à la place des «back-quotes» : date_aujourdhui=$(date) x <= y x>y x >= y -n x Les conditionnelles Chapterde 2 faire Scripting andlathe Shellde retour d’une commande : -z x Il est possible des tests sur valeur 1 2 3 4 5 6 x -le y x -gt y x -ge y – – x is less than or equal to y x is greater than y x is greater than or equal to y x is not null 6 x is null #!/usr/bin/bash if uname -a | grep "GNU/Linux" >/dev/null then echo un geek" bash shines in its options Table 2.2"Vous showsetes the bash comparison operators for numbers and strings. bash for evaluating the properties of fi else echo "Vous n'etes pas un geek" its /bin/test legacy). Table for strings, exactly the 2.3 shows a few of bash’s many f fiuses textual operators for numbers and symbolic operators comparison operators. of Perl. Si leopposite grep ne trouve pas la chaîne recherchée, la valeur de retour est fausse. En mode interactif, il est possible d’obtenir la valeur de retour d’une commande à l’aide de «$?». Les conditions Table 2.2 Elementary bash comparison operators Table 2.3 bash file evaluation operators String Numeric x=y x != y x<y x <= y x>y x >= y -n x -z x x -eq y x -ne y x -lt y x -le y x -gt y x -ge y – – True if x is equal to y x is not equal to y x is less than y x is less than or equal to y x is greater than y x is greater than or equal to y x is not null x is null Operator -d file -e file -f file -r file -s file -w file file1 -nt file2 file1 -ot file2 True if file exists and is a directory file exists file exists and is a regular file You have read permission on file file exists and is not empty You have write permission on file file1 is newer than file2 file1 is older than file2 NFA003 – P-F.B. 1 if [ -d "$nom" ] 1 if [ ! -d "$nom" ] of files (again, courtesy then shines in its options for evaluating the properties Although 2 bash # !the indique la négation 2 then elif form isofuseful, a case selection is often a b echo "$nom est un repertoire" 3 its echo "$nom n'est pas un repertoire" 3 /bin/test legacy). Table 2.3 shows a few of bash’s many file-testing and fileIts syntax is shown below in a sample routine that centraliz 4 fi 4 fi comparison operators. Table 2.3 bash file evaluation operators Operator -d file True if file exists and is a directory Of particular note are the closing parenthesis after each co semicolons that follow the statement block to be executed w met. The case statement ends with esac. # The log level is set in the global variable LOG_LEVEL. # are, from most to least severe, Error, Warning, Info, a Les Redirections d’E/S 7 Chaque programme Unix dispose de : □ une entrée standard, stdin ; □ une sortie standard, stdout ; □ une sortie d’erreur standard, stderr ; Redirection de la sortie standard ▷ vers un fichier avec création : ▷ vers un fichier avec ajout : ▷ vers une commande : 1 echo "toto" > mon_fichier.txt 1 echo "toto" >> mon_fichier.txt 1 echo "toto" | grep "o" Redirection de l’entrée standard ▷ depuis un fichier : ▷ depuis le résultat d’un shell : 1 mon_programme < mon_fichier.txt 1 cat <(ls) Redirection de la sortie standard d’erreur NFA003 – P-F.B. ▷ vers un descripteur de suppression : ▷ vers le canal de sortie standard 1 ma_commande 2> /dev/null 1 mon_programme 2>&1 Les boucles 8 Répéter une commande sur un ensemble de valeurs : La variable «i» va prendre une valeur de l’ensemble des fichier indiqués par 1 #!/usr/bin/bash 2 «*.c» pour chaque occurrence de la boucle. 3 for i in *.c 4 do stat "$i" Ensuite, la commande «stat» va être appliquée, avec le «do», sur chacune de 5 done ces occurrences. Recommendation Lorsque l’on écrit un script, il est recommandé de : ⋄ tester chaque combinaison de commande avant de l’inclure dans une boucle ; ⋄ afficher pour contrôle la commande finale obtenue après évaluation/substitution au lieu de l’exécuter : ⋄ puis une fois que la commande à l’air bien construite, et que tous les fichiers à traiter l’ont été correctement (bon chemin echo rm $fichier d’accès par exemple) on peut supprimer l’echo : rm $fichier Une manière inspirée des langages de programmation : 1 for (( i=0 ; i < $TAILLE ; i++ )) 2 do TABLE_VALEUR="$TABLE_VALEUR $i" 3 4 done Dans cet exemple, on utilise une boucle faisant varier un indice de 0 à TAILLE et chaque valeur de l’indice est ajouté dans une «table», c-à-d une chaîne de valeurs séparées par des espaces. 1 for i in $(seq 1 10) 2 do 3 echo "$i" 4 done Ici, la commande seq 1 10 retourne les valeurs de 1 à 10. NFA003 – P-F.B. Une manière plus élégante tirant parti des capacités du shell : Les boucles et les «ruptures de séquence» 9 Le break 1 #!/bin/bash 2 for f in * 3 do 4 file "$f" 5 echo -n "Continuer ?(o/n)" read saisie 6 if [ "$saisie" != "o" ] 7 then 8 break 9 fi 10 11 done Lors de la saisie de la réponse de l’utilisateur, si la chaîne lue est différente de «o», on arrête la boucle. Conseil L’utilisation du «break» est incontournable, même dans l’utilisation d’une boucle «while», car elle permet de simplifier la terminaison de la boucle, ce qui facilite la conception de script correct. NFA003 – P-F.B. Le continue 1 #!/bin/bash 2 for f in * 3 do 4 echo -n "Afficher contenu de $f ?(o/n)" 5 read saisie if [ "$saisie" != "o" ]; then 6 7 continue fi 8 9 cat "$f" 10 done Ici, l’usage du «continue» permet de simplifier l’écriture des conditionnelles : il permet de passer à l’occurence suivante de la boucle et de ne pas faire les commandes qui le suivent et qui devraient être incluses dans une structure «else». L’exécution séquentielle ou en «multi-tâche» 10 En séquentielle On sépare les commandes avec le caractère «;» : xterm $ cat mon_fichier.txt ; rm mon_fichier.txt Avec le «;» les commandes s’exécutent de manière séquentielle. L’écriture compacte On peut écrire le programme suivant de manière compactée grâce aux séparateurs de commandes «;» : 1 for f in $(ls) 2 do if [ -d "$f" ] 3 4 then echo "Repertoire : $f" 5 fi 6 7 done 1 2 3 4 5 for f in $(ls); do if [ -d "$f" ]; then echo "Repertoire : $f" fi done Exécution en parallèle ou en «multi-tâche» On sépare les commandes avec le caractère «&» : NFA003 – P-F.B. xterm $ gedit & cat mon_fichier.txt La commande gedit est lancée, puis sans en attendre la terminaison, la commande suivante est exécutée. Arithmétique 11 Attention : bash manipule les valeurs comme des chaîne de caractères. La façon d’utiliser une valeur peut changer sa nature (chaîne ou valeur numérique) : 1 2 3 4 5 6 7 #!/bin/bash a=1 b=$((2)) c=$a+$b d=$(($a+$b)) echo "$a + $b = $c \t(signe plus comme caractère)" echo "$a + $b = $d \t(signe plus comme opérateur)" xterm 1 + 2 = 1+2 (signe plus comme caractère) 1 + 2 = 3 (signe plus comme opérateur) Pour «convertir» une valeur en numérique, il faut la mettre entre «$((...))». Conseil 1 2 3 4 5 6 7 #!/bin/bash counter=1 while read line do echo "$counter: $line" $((counter++)) done Utilisation de la commande bc : xterm $ resultat=$(echo "546/4+3" | bc) $ echo $resultat NFA003 – P-F.B. 139 Avec echo "scale=2;546/4+3" on aurait obtenu la valeur 139.50 (2 chiffres après la virgule). Entrer des valeurs dans un script 12 Lire une valeur depuis stdin Il est possible d’entrer des valeurs depuis une saisie depuis «stdin» : 1 #!/bin/bash 2 echo -n "Entrer votre nom : " 3 read user_name 4 if [ -n "$user_name" ] then echo "Bonjour $user_name!" 5 6 exit 0 7 else echo "Vous ne m'avez pas dit votre nom !" 8 exit 1 9 10 fi Modifier stdin En lançant le script depuis le shell : xterm $ ./mon_script < fichier.txt Ou bien avec un «pipe» : xterm $ cat fichier.txt | ./mon_script NFA003 – P-F.B. Dans ces deux cas, l’opération «read» du script récupérera ses données, ligne par ligne, depuis stdin que celui-ci soit remplacé par le contenu d’un fichier ou bien la sortie d’une autre commande. Traitement par ligne 13 Lecture du contenu d’un fichier Il faut faire attention aux notions de : □ ligne : c-à-d à la présence d’un caractère \n à la fin de chaque ligne ; □ fin de fichier : lorsque le fichier prend fin alors on obtient un caractère EOF, «End Of Line» ; On va utiliser la structure «while» sur la condition de lecture d’une ligne : à la fin d’un fichier, la lecture est finie et la ligne lue est vide, c-à-d assimilable à la valeur «faux» : 1 2 3 4 5 6 #!/bin/bash fichier="boucle_lecture" while read ligne do echo $ligne done <$fichier Lecture du résultat d’une commande 1 2 3 4 5 6 #!/bin/bash fichier="boucle_lecture" while read ligne do echo "[$ligne]" done < <(cat $fichier) NFA003 – P-F.B. Ici, on lit ligne par ligne le résultat du shell exécutant la commande cat $fichier . Les arguments xterm $ ./mon_script 10 "toto" ▷ ▷ ▷ ▷ la variable «$#» contient le nombre d’arguments ; «$0» est le nom de la commande ; «$1» est le premier argument, «$2» le second, etc ; «$*» tous les arguments de celui de rang 1 au dernier. Exemple Soit le script «dis_bonjour_a» 1 #!/bin/bash 2 echo "bonjour $1" xterm $ bash dis_bonjour_a toto bonjour toto On verra que les arguments s’utilisent de la même manière dans le cadre des fonctions. La valeur de retour d’un script ou d’une commande La valeur de succès ou d’échec de l’exécution d’une commande est obtenue avec la variable «$?» : xterm $ ls toto ls: impossible d'accéder à toto: Aucun fichier ou dossier de ce type NFA003 – P-F.B. $ echo $? 2 14 Les fonctions 1 #!/bin/bash 2 function documentation { echo "Usage: $0 rep_source rep_destination" 3 exit 1 4} 5 if [ $# -ne 2 ]; 6 then documentation 7 else # Il y a 2 arguments 8 if [ -d $1 ]; then 9 source=$1 10 else 11 echo 'Mauvais repertoire source' 12 documentation 13 fi 14 if [ -d $2 ]; then 15 destination=$2 16 echo 'Mauvais repertoire destination' 17 documentation 18 else 19 fi 20 fi 21 printf "Repertoire source : ${source}\n" 22 printf "Repertoire destination : ${destination}\n" 15 NFA003 – P-F.B. La fonction «documentation» indique le mode d’emploi du script et le «exit 1» indique une erreur en résultat de l’exécution du script. Pour indiquer un succès on retourne la valeur 0. Fonctions avec arguments 1 function documentation { echo "Usage: $0 rep_source rep_destination" 2 if [ $# -eq 0 ]; then 3 exit 99 # Une valeur quand il n'y a pas d'argument passé à la fonction 4 5 else exit $1 # la valeur donnée en argument 6 7 fi } 8 Les arguments de la fonction sont gérés de la même façon que ceux du script. Au niveau de l’appel de la fonction NFA003 – P-F.B. 1 documentation 36 # demandera à la fonction de sortir avec la valeur 36 16 2 La commande grep 17 Elle utilise des expressions régulières pour faire des recherches dans les lignes d’un fichier. Syntaxe des options ⋄ ⋄ ⋄ ⋄ ⋄ ⋄ ⋄ ⋄ -c : compte le nombre de correspondances ; -i : cherche sans tenir compte de la «casse» des caractères (sans différences majuscules/minuscules) ; -l : affiche le nom des fichiers où la correspondance se produit ; -n : affiche le numéro de ligne où la correspondance se produit ; -r : cherche de manière récursive dans les sous-répertoires ; -v : montre le contenu qui ne correspond pas au motif de recherche ; -o : montre uniquement le contenu correspondant à l’expression régulière ; -E : interprète l’expression régulière sous une forme étendue, voir transparent suivant sur les ER). Exemple ∘ cherche la chaîne chat dans tous les fichiers : xterm $ grep "chat" * ∘ cherche de manière récursive les lignes ne contenant pas «chat» sans tenir compte de la casse : NFA003 – P-F.B. xterm $ grep -irlv "chat" * ∘ cherche le nombre de fichiers qui contiennent «chat» mais ne terminant pas par «.log» xterm $ grep -ilr "chat" * | grep -cv "\.log$" ∘ cherche une expression régulière dans un document : xterm $ grep -E "^[0-9]+" mon_fichier.txt Recherche les lignes commençant par un nombre. 3 Expressions régulières ou expressions rationnelles 18 Une ER permet de faire de l’appariement de motif, pattern matching : il est possible de savoir si un motif est présent dans une chaîne, mais également comment il est présent dans la chaine (en mémorisant la séquence correspondante). Une expression régulière est exprimée par une suite de meta-caractères, exprimant : ▷ une position pour le motif ▷ une alternative ^ : début de chaîne | : ceci ou cela, exemple : a|b $ : fin de chaîne ▷ un caractère .: [ ]: [^ ] : [a-zA-Z] : n’importe quel caractère un caractère au choix parmi une liste, exemple : [ABC] tous les caractères sauf..., exemple : [^@] tout sauf le «@» toutes les lettres minuscules et majuscules ▷ des quantificateurs, qui permettent de répéter le caractère qui les précédent : NFA003 – P-F.B. * : zéro, une ou plusieurs fois + : une ou plusieurs fois { n } : n fois ? : zéro ou une fois { n, m } : entre n et m fois ▷ des familles de caractères : [0-9] : un chiffre [^0-9] : tout sauf un chiffre \n newline [\ \t ] : un espace [A-Za-z0-9] : un caractère alphanumérique \r retour-chariot Attention : l’algorithme d’appariement de de motif est glouton : il recherche la plus longue chaîne possible. 4 La commande sed 19 La commande sed permet : ▷ de faire de la substitution : xterm $ cat fichier.txt | sed 's/ville/Limoges/g' réalise la substition du mot «ville» par le mot «Limoges», c-à-d de la zone 1 par la zone 2 dans les pararmètres de sed /1/2/ . Le paramètre «g» indique de faire la substition dans toute la ligne et pas seulement sur la première occurence. ▷ d’utiliser des expressions régulières mais sans le quantificateur «+» : xterm $ echo 100 300 | sed 's/[0-9]*/42/g' 42 42 remplace tout nombre par la valeur 42 ▷ de faire de la mémorisation de motif en entourant avec des parenthèses «()» : xterm $ echo 100 300 | sed -E 's/([0-9]+)/val: \1/g' val: 100 val: 300 ici, le motif entouré de parenthèse est numéroté (il peut y en avoir plusieurs) puis utilisé avec la notation «\1» ▷ de la sélection améliorée, c-à-d où l’on peut récupérer uniquement la partie du motif trouvé : NFA003 – P-F.B. xterm $ echo -100- 300 | sed -nE 's/-([0-9]+)-.*/val:\1/gp' val:100 ici, le motif est trouvé grâce à la présence des «- -», qui sont ensuite éliminés du résultat. Exemple d’utilisation de sed 20 Récupération de l’extension d’un fichier xterm $ echo "mon_fichier.tar.gz" | sed -E 's/.*\.([^\.]+)/\1/' gz Il faut faire une expression régulière : □ basée sur l’idée que l’extension est «une séquence de caractères ne contenant pas de point et séparée par un point» ; □ traitant la totalité de la ligne ; □ mémorisant l’extension recherchée ; □ sachant que l’appariement de motif est «glouton» : il cherche à créer la séquence la plus longue. Pour récupérer le nom du fichier sans l’extension xterm $ echo "mon_fichier.tar.gz" | sed -E 's/(.*)\.[^\.]+/\1/' mon_fichier.tar On remarquera que si le nom de fichier ne contient pas d’extension : xterm $ echo "mon_fichier" | sed -E 's/^(.*)\.[^\.]+$/\1/' mon_fichier NFA003 – P-F.B. Vu que la substition ne s’applique pas, la chaîne d’entrée traverse sed sans modification. xterm $ echo "mon_fichier" | sed -nE 's/(.*)\.[^\.]+/\1/p' Ici, on impose à sed : − de ne sortir que les lignes pour lesquelles une modification a eu lieu avec le «-n» ; − de sortir les lignes où la modification a eu lieu avec la commande «p» finale. 5 La commande awk 21 Awk est adaptée au traitement de données structurées, c-à-d sur une même ligne et séparées par un même séparateur. Il dispose d’un langage de script et d’expressions régulières. Utilisation directe xterm $ ls -l mon_fichier -rwxr-xr-x 1 pef staff 369 Mar 15 11:13 mon_fichier $ ls -l mon_fichier | awk '{ print $7 " " $6 }' 15 Mar Le séparateur est l’espace et chaque élément de la ligne est numéroté ; la commande «print» permet d’afficher l’élément que l’on désire («$0» correspond à la ligne complète). L’argument de awk est mis entre «'» pour éviter les substitutions du shell. Utilisation avancée ▷ changement de séparateur : xterm $ echo "nom_prenom" | awk -F_ '{ print $2 " "$1 }' prenom nom ▷ réagir sur la première ligne avec BEGIN et la dernière ligne avec END : xterm $ ls | wc -l 12 NFA003 – P-F.B. $ ls | awk 'BEGIN {x=0} {x=x+1} END {print x}' 12 Le premier bloc «{}» n’est fait que sur la première ligne, le second pour toutes les lignes et le troisième uniquement sur la dernière ligne. La commande awk 22 Le format de commande est le suivant : pattern { action } où «pattern» est une expression régulière ou les mots réservés BEGIN ou END. Exemple : récupérer les lignes qui commencent par un numéro de téléphone xterm $ cat fichier_exemple xterm $ cat fichier_exemple | awk '/^[0-9]/ { print $2 }' # commentaire Alice 0123456789 Alice Bob 0632789750 Bob Cedric # un autre commentaire $ cat fichier_exemple | awk '/^06/ { print $2 }' 0775897895 Cedric Bob NFA003 – P-F.B. Utilisation d’une action par défaut et du next : xterm $ cat fichier_exemple | awk '/^06/ {print $2 "->portable"; next} /^#/ {next} {print $2 "->fixe"}' Alice->fixe Bob->portable Cedric->fixe L’utilisation du «next», en et , permet de passer à la ligne suivante et ainsi, de ne pas traiter le prochain couple (motif, action). La dernière action, , ne contient pas de motif : c’est l’action par défaut. Utilisation d’une commande externe : calcul de MD5 sur le numéro de téléphone xterm $ cat fichier_exemple | awk '/^[0-9]/ { system("echo "$1" | md5") }' 3749f52bb326ae96782b42dc0a97b4c1 0d3515154e5d09e146e5b614dab4b7c3 11d2782f70ad0b979abf9cc5c5c782fe La commande awk □ le nombre de champs d’une ligne : 23 xterm $ cat champs.txt xterm $ cat champs.txt | awk '{ if (NF==2) { print "Deux champs :" un deux $1 " et " $2 }}' 1 deux trois Deux champs :un et deux La variable NF, «Number of Fields», donne le nombre de champs trouvés avec le séparateur utilisé. xterm On utilise $NF pour afficher le dernier champs, c-à-d $ cat champs.txt | awk '{ print $NF }' le champs qui a pour numéro «NF». deux trois □ le nombre de ligne avec «NR», «number of records» : xterm $ cat champs.txt | awk 'END { print NR }' □ on peut mettre une condition à la place de la pattern : On affiche le numéro de la ligne à la fin, qui correspond au nombre de lignes total. NFA003 – P-F.B. xterm $ cat champs.txt | awk '(NF==2) { print "Deux champs :" $1 " et " $2 } (NF==3) { print "Autre"} ' Deux champs :un et deux Autre □ On a ainsi deux actions en fonction de la valeur de NF. passer à la ligne suivante avec la fonction getline : xterm $ cat champs.txt | awk '{getline; print $0}' 1 deux trois La commande awk 24 Les tables associatives Une liste associative est une tableau dont l’index est une chaîne et la valeur associée quelconque : ▷ un index non défini est associé à la valeur 0 ; ▷ la définition de l’association (index,valeur) se fait par affectation : tab[index]=valeur ; ▷ la suppression de l’association : delete tab[index] Exemple : trouver la liste des mots uniques d’un fichier et leur nombre d’occurence : xterm $ cat champs.txt | awk '{for (i=1;i<=NF;i++) {liste_mots[$i]++}} END { for (un_mot in liste_mots) { print un_mot "->" liste_mots[un_mot]}}' trois->1 deux->2 un->1 1->1 NFA003 – P-F.B. On utilise une table associative «liste_mots» dont l’index est un mot de la ligne parcouru à l’aide de la première boucle et la valeur associée est le nombre d’occurence : au début elle est indéfini ou égale à zéro, puis elle est incrémentée avec le «++». À la fin, on parcours la liste des index avec la seconde boucle for et on affiche le nombre d’occurences. 6 Combinaison de shell et de la commande find 25 Renommer récursivement tous les fichiers d’extension «.c» en «.C» : 1 2 3 4 5 6 #!/usr/bin/bash find . -type f -name '*.c' | while read fname do # on mets un echo devant la commande pour tester le script echo mv $fname ${fname/.c/.C/}; done Ici, on utilise la commande «find» pour rechercher tous les fichiers dans le répertoire local et récursivement dans l’ensemble des sous-répertoires. La sortie de la commande «find» est «pipée» dans une boucle «while» de bash où chaque sortie va être lue et stockée dans la variable «fname». Enfin, on réalise un echo de la commande où l’on réalise une substitution de «.c» en «.C» sur la variable $fname à l’aide de la notation «${fname/.c/.C/}». Remarque Il n’est pas nécessaire de connaitre toutes les possibilités de bash, comme ici pour la substitution d’une sous-chaîne par une autre. Au contraire, il est préférable d’utiliser des commandes externes, ce qui permet de «mutualiser» la connaissance. Autre façon d’écrire le script : NFA003 – P-F.B. 1 2 3 4 5 6 #!/usr/bin/bash while read fname do # on mets un echo devant la commande pour tester le script echo mv $fname ${fname/.c/.C/}; done < <(find . -type f -name '*.c') Combinaison de shell et de la commande find On peut utiliser les commandes basename et dirname : 1 2 3 4 5 6 #!/usr/bin/bash find . -type f -name '*.c' | while read fname; do new_name=`basename $fname .c` rep=`dirname $fname` echo mv $fname "$rep/$new_name.C"; done Ou, pour l’obtention des fichiers «.c» uniquement dans le répertoire courant : 1 #!/usr/bin/bash 2 for fname in *.c 3… Ou, en utilisant directement le résultat de la commande find, mais en consommant plus de place mémoire : 1 for fname in `find . -type f -name "*.c"`; 2… En utilisant la commande «sed» pour réaliser la substitution : 1 2 3 4 for fname in `find . -type f -name "*.c"`; do new_name=`echo $fname | sed -e "s/\.c/\.C/"` echo mv $fname $new_name; done On teste pour améliorer la commande : xterm $ echo "toto.c.c" | sed -e "s/\.c$/\.C/" Ou dans le cadre d’un projet : NFA003 – P-F.B. xterm $ echo "toto.89.c.10" | awk -F. '{print $NF}' 26 7 Opérations avancées : décomposition nom fichier & descripteur de fichier 27 Récupérer le nom d’un fichier et son extension donné en paramètre Pour rappel, la variable «$1» contient la valeur du premier argument du script. 1 2 3 4 5 #!/bin/bash nomfichier="${1%.*}" extension="${1##*.}" echo "Nom fichier $nomfichier" echo "Extension fichier $extension" xterm pef@darkstar:~$ ./test_extraction mon_fichier.txt Nom fichier mon_fichier Extension fichier txt Lire ou écrire vers un descripteur de fichier particulier ▷ Pour écrire sur le descripteur de fichier n°4 : xterm echo "Hello world" >&4 ▷ Pour lire une ligne depuis le descripteur de fichier n°3 et l’afficher sur STDOUT : xterm head -n 1 <&3 ▷ Pour lire une ligne depuis le descripteur de fichier 3 et la stocker dans une variable : NFA003 – P-F.B. xterm read saisie <&3 ▷ valeur="${saisie:-$valeur_defaut}" qui affecte à la variable valeur le contenu de la variable saisie si celui-ci est défini (différent d’une ligne vide) ou bien de la variable valeur_defaut sinon. Opérations avancées : les tableaux L’utilisation de tableau ▷ initialisation direct du contenu du tableau : xterm $ declare -a MON_TABLEAU $ MON_TABLEAU=( un deux trois ) ▷ remplissage élément par élément : 1 2 3 4 5 6 7 8 declare -a MON_TABLEAU X=0 # initialisation du compteur a zero for ELEMENT in un deux trois do MON_TABLEAU[$X]=$ELEMENT ((X=X+1)) done echo ${MON_TABLEAU[*]} # affiche la totalite du tableau ▷ accèder à un élément du tableau : NFA003 – P-F.B. echo ${MON_TABLEAU[0]} # afficher le premier element 28