Awk est un langage de manipulation de texte plein de fonctionnalités avec une syntaxe proche du C. Alors qu'il possède un ensemble impressionnant d'opérateurs et de fonctionnalités, nous n'en couvrirons que quelques-uns, les plus utiles pour l'écriture de scripts shell.
Awk casse chaque ligne d'entrée en champs. Par défaut, un champ est une chaîne de caractères consécutifs délimités par des espaces, bien qu'il existe des options pour changer le délimiteur. Awk analyse et opère sur chaque champ. Ceci rend awk idéal pour gérer des fichiers texte structurés, particulièrement des tableaux, des données organisées en ensembles cohérents, tels que des lignes et des colonnes.
Des guillemets forts (guillemets simples) et des accolades entourent les segments de code awk dans un script shell.
awk '{print $3}' $nomfichier # Affiche le champ #3 du fichier $nomfichier sur stdout. awk '{print $1 $5 $6}' $nomfichier # Affiche les champs #1, #5 et #6 du fichier $nomfichier. |
Nous venons juste de voir la commande awk print en action. Les seules autres fonctionnalités de awk que nous avons besoin de gérer ici sont des variables. Awk gère les variables de façon similaire aux scripts shell, quoiqu'avec un peu plus de flexibilité.
{ total += ${numero_colonne} } |
END { print total } |
Correspondant au END, il existe BEGIN, pour un bloc de code à exécuter avant que awk ne commence son travail sur son entrée.
Pour des exemples de awk à l'intérieur de scripts shell, jetez un oeil sur:
C'est tout ce que nous allons voir sur awk, mais il existe bien plus à apprendre. Voyez les références appropriées dans la Bibliographie.
Précédent | Sommaire | Suivant |
Sed | Niveau supérieur | Codes de sortie ayant une signification particulière |
#!/bin/bash SUCCESS=0 E_BADINPUT=65 test "$1" -ne 0 -o "$1" -eq 0 2>/dev/null # Un entier est soit égal à 0 soit différent de 0. # 2>/dev/null supprime les messages d'erreur. if [ $? -ne "$SUCCESS" ] then echo "Usage: `basename $0` integer-input" exit $E_BADINPUT fi let "sum = $1 + 25" # Donnera une erreur si $1 n'est pas un entier. echo "Sum = $sum" # Toute variable, pas simplement un paramètre de ligne de commande, peut être #+ testé de cette façon. exit 0
L'échelle 0 - 255 des valeurs de retour des fonctions est une limitation importante. Les variables globales et autres moyens de contourner ce problème sont souvent des problèmes en eux-même. Une autre méthode, pour que la fonction communique une valeur de retour au corps principal du script, est que la fonction écrive sur stdout la << valeur de sortie >>, et d'assigner cette sortie à une variable.
Exemple 34-10. Astuce de valeur de retour
#!/bin/bash # multiplication.sh multiplie () # Multiplie les paramètres passés. { # Acceptera un nombre variable d'arguments. local produit=1 until [ -z "$1" ] # Jusqu'à la fin de tous les arguments... do let "produit *= $1" shift done echo $produit # N'affichera pas sur stdout, } #+ car cela va être affecté à une variable. mult1=15383; mult2=25211 val1=`multiplie $mult1 $mult2` echo "$mult1 X $mult2 = $val1" # 387820813 mult1=25; mult2=5; mult3=20 val2=`multiplie $mult1 $mult2 $mult3` echo "$mult1 X $mult2 X $mult3 = $val2" # 2500 mult1=188; mult2=37; mult3=25; mult4=47 val3=`multiplie $mult1 $mult2 $mult3 $mult4` echo "$mult1 X $mult2 X $mult3 X mult4 = $val3" # 8173300 exit 0 |
La même technique fonctionne aussi pour les chaînes de caractères alphanumériques. Ceci signifie qu'une fonction peut << renvoyer >> une valeur non-numérique.
capitaliser_ichar () # Capitaliser le premier caractère { #+ de(s) chaîne(s) de caractères passées. chaine0="$@" # Accepte de multiples arguments. premiercaractere=${chaine0:0:1} # Premier caractère. chaine1=${chaine0:1} # Reste de(s) chaîne(s) de caractères. PremierCaractere=`echo "$premiercaractere" | tr a-z A-Z` # Capitalise le premier caractère. echo "$PremierCaractere$chaine1" # Sortie vers stdout. } nouvellechaine=`capitalize_ichar "chaque phrase doit commencer avec une lettre majuscule."` echo "$nouvellechaine" # Chaque phrase doit commencer avec une lettre majuscule. |
Il est même possible pour une fonction de << renvoyer >> plusieurs valeurs avec cette méthode.
Exemple 34-11. Une astuce permettant de renvoyer plus d'une valeur de retour
#!/bin/bash # sum-product.sh # Une fonction peut "renvoyer" plus d'une valeur. somme_et_produit () # Calcule à la fois la somme et le produit des arguments. { echo $(( $1 + $2 )) $(( $1 * $2 )) # Envoie sur stdout chaque valeur calculée, séparée par un espace. } echo echo "Entrez le premier nombre " read premier echo echo "Entrez le deuxième nombre " read second echo valretour=`somme_et_produit $premier $second` # Affecte à la variable la sortie #+ de la fonction. somme=`echo "$valretour" | awk '{print $1}'` # Affecte le premier champ. produit=`echo "$valretour" | awk '{print $2}'`# Affecte le deuxième champ. echo "$premier + $second = $somme" echo "$premier * $second = $produit" echo exit 0 |
Ensuite dans notre liste d'astuces se trouvent les techniques permettant de passer un tableau à une fonction, << renvoyant >> alors un tableau en retour à la fonction principale du script.
Le passage d'un tableau nécessite de charger des éléments séparés par un espace d'un tableau dans une variable avec la substitution de commandes. Récupérer un tableau comme << valeur de retour >> à partir d'une fonction utilise le stratagème mentionné précédemment de la sortie (echo) du tableau dans la fonction, puis d'invoquer la substitution de commande et l'opérateur ( ... ) pour l'assigner dans un tableau.
Exemple 34-12. Passer et renvoyer un tableau
#!/bin/bash # array-function.sh: Passer un tableau à une fonction et... # "renvoyer" un tableau à partir d'une fonction Passe_Tableau () { local tableau_passe # Variable locale. tableau_passe=( `echo "$1"` ) echo "${tableau_passe[@]}" # Liste tous les éléments du nouveau tableau déclaré #+ et initialisé dans la fonction. } tableau_original=( element1 element2 element3 element4 element5 ) echo echo "tableau_original = ${tableau_original[@]}" # Liste tous les éléments du tableau original. # Voici une astuce qui permet de passer un tableau à une fonction. # ********************************** argument=`echo ${tableau_original[@]}` # ********************************** # Emballer une variable #+ avec tous les éléments du tableau original séparés avec un espace. # # Notez que d'essayer de passer un tableau en lui-même ne fonctionnera pas. # Voici une astuce qui permet de récupérer un tableau comme "valeur de retour". # ***************************************** tableau_renvoye=( `Passe_Tableau "$argument"` ) # ***************************************** # Affecte une sortie de la fonction à une variable de type tableau. echo "tableau_renvoye = ${tableau_renvoye[@]}" echo "=============================================================" # Maintenant, essayez encore d'accèder au tableau en dehors de la #+ fonction. Passe_Tableau "$argument" # La fonction liste elle-même le tableau, mais... #+ accèder au tableau de l'extérieur de la fonction est interdit. echo "Tableau passé (de l'intérieur de la fonction) = ${tableau_passe[@]}" # Valeur NULL comme il s'agit d'une variable locale. echo exit 0 |
Pour un exemple plus élaboré du passage d'un tableau dans les fonctions, voir Exemple A-11.
En utilisant la construction en double parenthèses, il est possible d'utiliser la syntaxe style C pour initialiser et incrémenter des variables ainsi que dans des boucles for et while. Voir Exemple 10-12 et Exemple 10-17.
Une technique de scripts utile est d'envoyer de manière répétée la sortie d'un filtre (par un tuyau) vers le même filtre, mais avec un ensemble différent d'arguments et/ou options. Ceci est spécialement intéressant pour tr et grep.
# De l'exemple "wstrings.sh". wlist=`strings "$1" | tr A-Z a-z | tr '[:space:]' Z | \ tr -cs '[:alpha:]' Z | tr -s '\173-\377' Z | tr Z ' '` |
Exemple 34-13. Un peu de fun avec des anagrammes
#!/bin/bash # agram.sh: Jouer avec des anagrammes. # Trouver les anagrammes de... LETTRES=etaoinshrdlu anagram "$LETTRES" | # Trouver tous les anagrammes de cet ensemble de lettres... grep '.......' | # Avec au moins 7 lettres, grep '^is' | # commençant par 'is' grep -v 's$' | # sans les puriels grep -v 'ed$' # Sans verbe au passé ("ed" en anglais) # Utilise l'utilitaire "anagram" #+ qui fait partie du paquetage de liste de mots "yawl" de l'auteur. # http://ibiblio.org/pub/Linux/libs/yawl-0.2.tar.gz exit 0 # Fin du code. bash$ sh agram.sh islander isolate isolead isotheral |
Voir aussi Exemple 28-2, Exemple 12-18, et Exemple A-10.
Utiliser des << documents anonymes >> pour mettre en commentaire des blocs de code, pour ne pas avoir à mettre en commentaire chaque ligne avec un #. Voir Exemple 17-10.
Lancer sur une machine un script dépendant de la présence d'une commande qui peut être absente est dangereux. Utilisez whatis pour éviter des problèmes potentiels avec ceci.
CMD=commande1 # Premier choix. PlanB=commande2 # Option en cas de problème. command_test=$(whatis "$CMD" | grep 'nothing appropriate') # Si 'commande1' n'est pas trouvé sur ce système, 'whatis' renverra #+ "commande1: nothing appropriate." if [[ -z "$command_test" ]] # Vérifie si la commande est présente. then $CMD option1 option2 # Lancez commande1 avec ses options. else # Sinon, $PlanB #+ lancez commande2. fi |
La commande run-parts est utile pour lancer un ensemble de scripts dans l'ordre, particulièrement en combinaison avec cron ou at.
Il serait bien d'être capable d'invoquer les objets X-Windows à partir d'un script shell. Il existe plusieurs paquets qui disent le faire, à savoir Xscript, Xmenu, et widtools. Les deux premiers ne semblent plus maintenus. Heureusement, il est toujours possible d'obtenir widtools ici.
![]() | Le paquet widtools (widget tools, outils pour objets) nécessite que la bibliothèque XForms soit installée. De plus, le Makefile a besoin d'être édité de façon judicieuse avant que le paquet ne soit construit sur un système Linux typique. Finalement, trois des six objets offerts ne fonctionnent pas (en fait, ils génèrent un défaut de segmentation). |
Pour plus d'efficacité des scripts utilisant des widgets, essayez Tk ou wish (des dérivés de Tcl), PerlTk (Perl avec des extensions Tk), tksh (ksh avec des extensions Tk), XForms4Perl (Perl avec des extensions XForms), Gtk-Perl (Perl avec des extensions Gtk), ou PyQt (Python avec des extensions Qt).
Précédent | Sommaire | Suivant |
Optimisations | Niveau supérieur | Problèmes de sécurité |
--
Bash supporte uniquement les tableaux à une dimension, néanmoins une petite astuce permet de simuler des tableaux à plusieurs dimensions.
Exemple 26-9. Simuler un tableau à deux dimensions, puis son test
#!/bin/bash # Simuler un tableau à deux dimensions. # Un tableau à deux dimensions stocke les lignes séquentiellement. Lignes=5 Colonnes=5 declare -a alpha # char alpha [Lignes] [Colonnes]; # Déclaration inutile. charge_alpha () { local rc=0 local index for i in A B C D E F G H I J K L M N O P Q R S T U V W X Y do local ligne=`expr $rc / $Colonnes` local colonne=`expr $rc % $Lignes` let "index = $ligne * $Lignes + $colonne" alpha[$index]=$i # alpha[$ligne][$colonne] let "rc += 1" done # Un peu plus simple # declare -a alpha=( A B C D E F G H I J K L M N O P Q R S T U V W X Y ) # mais il manque néanmoins le "bon goût" d'un tableau à deux dimensions. } affiche_alpha () { local ligne=0 local index echo while [ "$ligne" -lt "$Lignes" ] # Affiche dans l'ordre des lignes - do # les colonnes varient # tant que ligne (la boucle externe) reste # identique local colonne=0 while [ "$colonne" -lt "$Colonnes" ] do let "index = $ligne * $Lignes + $colonne" echo -n "${alpha[index]} " # alpha[$ligne][$colonne] let "colonne += 1" done let "ligne += 1" echo done # Un équivalent plus simple serait # echo ${alpha[*]} | xargs -n $Colonnes echo } filtrer () # Filtrer les index négatifs du tableau. { echo -n " " if [[ "$1" -ge 0 && "$1" -lt "$Lignes" && "$2" -ge 0 && "$2" -lt "$Colonnes" ]] then let "index = $1 * $Lignes + $2" # Maintenan, l'affiche après rotation. echo -n " ${alpha[index]}" # alpha[$ligne][$colonne] fi } rotate () # Bascule le tableau de 45 degrés { # (le "balance" sur le côté gauche en bas). local ligne local colonne for (( ligne = Lignes; ligne > -Lignes; ligne-- )) # Traverse le tableau en # sens inverse. do for (( colonne = 0; colonne < Colonnes; colonne++ )) do if [ "$ligne" -ge 0 ] then let "t1 = $colonne - $ligne" let "t2 = $colonne" else let "t1 = $colonne" let "t2 = $colonne + $ligne" fi filtrer $t1 $t2 # Filtre les index négatifs du tableau. done echo; echo done # Rotation du tableau inspirée par les exemples (pp. 143-146) de # "Advanced C Programming on the IBM PC", par Herbert Mayer # (voir bibliographie). } #-----------------------------------------------------# charge_alpha # Charge le tableau. affiche_alpha # L'affiche. rotate # Le fait basculer sur 45 degrés dans le sens contraire des # aiguilles d'une montre. #-----------------------------------------------------# # C'est une simulation assez peu satisfaisante. # # Exercices: # --------- # 1) Réécrire le chargement du tableau et les fonctions d'affichage # + d'une façon plus intuitive et élégante. # # 2) Comprendre comment les fonctions de rotation fonctionnent. # Astuce: pensez aux implications de l'indexage arrière du tableau. exit 0 |
Un tableau à deux dimensions est essentiellement équivalent à un tableau à une seule dimension mais avec des modes d'adressage supplémentaires pour les références et les manipulations d'éléments individuels par la position de la << ligne >> et de la << colonne >>.
Pour un exemple encore plus élaboré de simulation d'un tableau à deux dimensions, voir Exemple A-11.
Précédent | Sommaire | Suivant |
Constructeurs de listes | Niveau supérieur | Fichiers |