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