Bash
Utilisation et syntaxe de scripts bash
<pdf>Fichier:Abs-guide.pdf</pdf>
Un script bash commence par #!/bin/bash (commentaire spécial) pour indiquer quel interpréteur de commandes utiliser. Il a par convention l'extension de fichier .sh.
Je peux le rendre exécutable avec chmod +x
Pour que mon script soit exécuté sans que j'ai besoin d'indiquer son chemin, il faut le placer dans un des répertoires qui sont stockés dans la variable système PATH, qui recense les lieux où se trouvent les exécutables. Je peux afficher son contenu par un echo $PATH
Les commentaires sont précédés de #
Variables
Il existe différents types de variables :
- Variables utilisateurs : Définies de manière locale dans le programme
- Variables d'environnement : Définies de manières globale (dans tout l'OS) et utilisables dans différents programmes
- Paramètres de position : À l'appel d'un script, ce sont les arguments passés en paramètres
Quelque soit le type de variable, on place toujours le caractère $ devant sa référence pour obtenir sa valeur; si j'ai var1, alors $var1 sera une référence à sa valeur.
Variables utilisateur
Une variable se déclare comme suit :
nom='variable'
Attention : Pas d'espaces autour du =. Un apostrophe peut être inséré dans la variable par un \, un retour de chariot avec \n. Et pour afficher une variable, on utilise echo $variable (avec l'option -e pour utiliser des \n). Cependant, on ne peut pas utiliser $variable dans des quotes ' '. Il existe plusieurs types de quotes:
- Les apostrophes ' : la variable n'est pas analysée et affichée comme telle
- Les guillemets " : la variable est analysée et son contenu est affiché; bash cherche des symboles spéciaux et les interprète.
- Les back quotes ` (AltGr + 7) : bash exécute ce qu'il trouve; les back quotes s'utilisent à la création de la variable : variable=`commande`
Les accolades {} permettent de délimiter le nom d'une variable au sein d'une chaîne de caractères : avec $prix=20:
- echo "Montant : $prix0 Euro"
- Ne fonctionne pas : on a pas de variable $prix0
- echo "Montant : ${prix}0 Euro"
- Affiche : Montant 200 Euro
La saisie utilisateur se fait avec la commande read.
- read nom : on affiche rien, on attend juste que l'utilisateur entre une valeur
- read nom prenom : on enregistre dans deux variables, avec deux valeurs attendues, si l'utilisateur donne plus, tout ce qui reste va dans la dernière variable.
- read -p 'Entrez une valeur' variable : On affiche un prompt, pour indiquer à l'utilisateur ce que l'on attend
- read -n 10 nom : On limite à 10 caractères. L'entrée s'arrête d'elle même arrivée à 10 caractères.
- read -t 10 variable : on limite le temps donné pour entrer la valeur (temps en secondes)
- read -s variable : on affiche pas le texte saisi par l'utilisateur
On peut également affecter le résultat d'une commande (son résultat sur la sortie standard, pas son code de retour) à une variable : pour cela il faut entourer la commande de parenthèses précédées du caractère $ :
repCourant=$(pwd)
echo "Vous êtes dans le répertoire $repCourant"
>Vous êtes dans le répertoire /home/justine
Variables d'environnement
Aussi appelées variables globales, une fois définies, elles sont disponibles dans tout le système. On peut les lister avec env. Certaines parmi les plus utiles sont :
SHELL
: indique quel type de shell est en cours d'utilisation (sh, bash, ksh…) ;
PATH
: une liste des répertoires qui contiennent des exécutables que vous souhaitez pouvoir lancer sans indiquer leur répertoire. Nous en avons parlé un peu plus tôt. Si un programme se trouve dans un de ces dossiers, vous pourrez l'invoquer quel que soit le dossier dans lequel vous vous trouvez ;
EDITOR
: l'éditeur de texte par défaut qui s'ouvre lorsque cela est nécessaire ;
HOME
: la position de votre dossier home
;
PWD
: le dossier dans lequel vous vous trouvez ;
OLDPWD
: le dossier dans lequel vous vous trouviez auparavant.
On peut les appeller dans notre script en utilisant leur nom :
echo "$HOME est votre dossier home!"
On peut en définir avec la commande export :
export NOM='Justine'
Les variables d'environnement ont une portée locale, c'est-à-dire qu'un script ne peut exporter des variables qu'à ses processus fils. Les processus fils ne peuvent pas réexporter des variables aux processus parents. Pour définir une variable d'environnement avec une portée plus globale, on utilise un export dans les fichier de configuration de profil utilisateur .bashrc ou .profile.
Variables des paramètres
Un script bash peut accepter des paramètres, données lorsque on l'appelle: ./script parametre1 parametre2
On peut y accéder via des variables qui sont crées automatiquement:
- $# : contient le nombre de paramètres
- $0 : contient le nom du script
- $1 : contient le premier paramètre
- ...
- $9 : contient le neuvième paramètre
Si on a plus de 9 paramètres, on utilise la commande shift. Après un usage de shift, le paramètre 1 prend la valeur du paramètre 2 , le 2 prend la valeur du 3, etc. On pourra utiliser shift dans une boucle et traiter les paramètres un par un.
Les tableaux
Comme une liste en python, un tableau contient plusieurs cases. On l'initialise comme suit :
tableau=('valeur0' 'valeur1' 'valeur2')
Pour accéder à une des "cases" du tableau, on fait comme ça:
${tableau[2]} #Affiche la valeur de la case 2, sachant qu'on compte comme d'habitude à partir de 0
${tableau[*]} #Affiche toutes les cases
On peut initialiser une case comme suit:
tableau[2]='valeur'
Opérations mathématiques
Bash traite toutes les variables comme des string, par défaut. Il faut utiliser la commande let pour faire des calculs:
- let "a = 5"
- let "b = 2"
- let "c = a + b"
- echo $c #renverra 7
Les opérations disponibles :
- + addition
- - soustraction
- multiplication
- / division
-
- puissance
-
- % modulo
On peut aussi contracter les commandes comme en python : let "a = a * 3" est équivalent à let "a *= 3".
let ne gère que les entiers, les décimaux sont gérés par la commande bc.
Conditions
If - then - else
La condition if - then - else en bash se fait sous la forme :
if [ condition ]
then
echo "C'est vrai"
else
echo "C'est faux"
fi
! : [ condition ] et pas [condition]
Une condition se vérifie avec = et pas ==
Tout ce qui est entre then et fi est exécuté; cependant, on peut avoir autant de elif que l'on veut, et le else est facultatif. Par exemple:
#!/bin/bash read -p 'Dites bonjour' reponse if [ $reponse = 'bonjour' ] then echo 'bonjour!' else echo 'Au revoir!' fi
On peut tester ce que l'on veut, y compris un paramètre; mais le script fonctionnera mal si on n'appelle pas de paramètre avec le script (il faudrait vérifier au préalable qu'il y'en a dans un autre if).
Elif
La condition elif existe aussi:
#!/bin/bash if [ $1 = 'camion' ] then echo 'Tut Tut!' elif [ $1 = 'moto' ] then echo 'Vroum Vroum!' elif [ $1 = 'voiture' ] then echo 'vrouuuuuuuuuum...' else echo 'Fallait dire camion...' fi
Tests
On peut effectuer trois types de tests:
- Sur des fichiers
- Sur des nombres,
- Sur des string.
Tests de string
- "$chaine1" = "$chaine2" : Les string sont-ils identiques?
- "$chaine1" != "$chaine2" : Les string sont-ils différents?
- -z "$chaine" : La chaîne est elle vide?
- -n "$chaine" : la chaîne est-elle non-vide?
- "$chaine1" $lt; "$chaine2" : chaine1 est-elle plus petite que chaine2 (ordre alphabétique ASCII)?
- "$chaine1" $gt; "$chaine2" : chaine1 est-elle plus grande que chaine2 (ordre alphabétique ASCII)?
Une variable non définie est considérée comme vide; cela vaut aussi pour les paramètres. De plus, si une variable vide n'est pas entourée de guillements, sa substitution donne du vide. C'est pour cela qu'il faut toujours entourer ses chaines de guillemets : un -n en a absolument besoin, et c'est de toute façon une bonne habitude.
Tests de nombres:
On peut faire des tests sur des nombres, si une variable en contient (même si bash traite toutes les variables comme des string en interne !). Ici 1 correspond à $variable1 et 2 correspond $variable2 (la flemme).
- 1 -eq 2 : Teste l'égalité des nombres, à ne pas confondre avec = utilisé pour les string.
- 1 -ne 2 : Teste si les chiffre sont différents (Not Equal), à ne pas confondre avec != pour les string
- 1 -lt 2 : Teste si 1 est plus petit que 2 (Lesser Than)
- 1 -le 2 : Teste si 1 est inférieur ou égal à 2 (Lesser or Equal)
- 1 -gt 2 : Teste si 1 est plus grand que 2 (Greater Than)
- 1 -ge 2 : Teste si 1 est supérieur ou égal à 2 (Greater or Equal)
Tests de fichiers
On peut tester des fichiers, à condition d'avoir leur chemin absolu ou relatif, qui peut être dans une variable. Ici f correspond à $fichier (re-flemme).
- -e f : Teste si le fichier existe
- -d f : vérifie si le fichier est un répertoire
- -f f : vérifie si le fichier est un fichier
- -L f : vérifie si le fichier est un lien symbolique
- -r f : vérifie sir le fichier est lisible
- -w f : vérifie si le fichier est modifiable
- -x f : vérifie si le fichier est exécutable
- f1 -nt f2 : vérifie si f1 est plus récent (Newer Than) que f2
- f1 -ot f2 : vérifie si f1 est plus vieux (Older Than) que f2
Exemple:
#!/bin/bash read -p 'Entrez le nom d\'un répertoire : ' rep if [ -d $rep ] then echo "Bravo!" else echo "Mais nooooon..." fi
Faire des AND et des OR et des NOT
Un AND se fait avec &&
Un OR se fait avec ||
Un NOT se fait plus ou moins avec un ! à mettre avant le test à inverser. (On inverse le test, un ! variable != 'valeur' équivaut à un test d'égalité)
Exemple:
- !/bin/bash
if [ $# -gt 0 ] && [ $1 = 'écureuil' ] && [ ! $1 = 'hibou' ]
then
echo "ACCESS GRANTED"
else
echo "ACCESS DENIED"
fi
Tester plusieurs conditions à la fois avec case
Un branchement multiple avec case permet de déterminer si la valeur sur laquelle ce branchement est effectué est égale à l'une des constantes. Si l'on a une correspondance, le bloc d'instructions associé sera exécuté; sinon bash exécutera le bloc correspondant à la constante par défaut, symbolisée par *) .
#!/bin/bash stVal="Jean" case $stVal in "Bruno") # Premier bloc d'instructions echo "Salut Bruno !" ;; "Michel") # Deuxième bloc d'instructions echo "Bien le bonjour Michel" ;; "Jean") # Troisième bloc d'instructions echo "Hé Jean, ça va ?" ;; *) # Bloc d'instructions par défaut echo "J'te connais pas, ouste !" ;; esac
Attention : le ;; dit à bash d'arrêter la lecture du case : il saute alors directement au esac. On peut aussi faire des ou dans un case, pas avec || mais avec | :
#!/bin/bash case $1 in "Chien" | "Chat" | "Souris") echo "C'est un mammifère" ;; "Moineau" | "Pigeon") echo "C'est un oiseau" ;; *) echo "Je ne sais pas ce que c'est" ;; esac
Quelques commandes
- echo : renvoie du texte sur la sortie standard.
- -e : active les caractères d'échappement
- -n : supprime le retour à la ligne
Boucles
While
Le principe est le même que dans les autres langages : tant que la condition n'est pas vérifiée, on exécute la boucle.
#!/bin/bash i=0 while [ $i -lt "10" ]; do # Bloc d'instructions ((i++)) echo $i done
For
Dans la boucle for ... in, la condition est exécutée autant de fois qu'il y'a de valeurs en entrée. À chaque itération, le bloc s'exécutera avec chacune des valeurs.
#!/bin/bash vals='/mnt /dev /proc /sys /tmp /usr/bin /var/tmp' for i in $vals; do # Bloc d'instructions echo $i # 1er Passage avec "/mnt", 2ème avec "/dev", 3ème avec "/proc"... done
On peut bien sûr aussi utiliser ce genre de boucles en générant des valeurs. Tant qu'il y'a des valeurs à générer, on les utilise pour tester la condition :
#!/bin/bash for ((i = 10; i >= 0; i -= 1)); do # Bloc d'instructions echo $i done
Ici, on a i == 10, et tant qu'il est >= à 0, on le décrémente de 1.
IFS (Internal Field Separator)
Lors de l'exécution d'une boucle for, bash utilise la valeur de la variable IFS (espace, tabulation, retour à la ligne) pour savoir comment isoler dans la chaîne d'entrée chacun des éléments constituants de la boucle (comme avec le premier exemple de boucle for donné plus haut : chacun des éléments est séparé des autres par un espace). On peut cependant avoir un problème quand l'un des éléments doit contenir un espace :
BASH $> vals='/mnt /dev /proc /sys /tmp /usr/bin /home/joda/Mes Documents' BASH $> for i in $vals; do echo $i; done /mnt /dev /proc /sys /tmp /usr/bin /home/joda/Mes Documents BASH $>
Il faut alors changer la valeur de la variable IFS avec un caractère choisi; dans cet exemple, il est plus approprié d'utiliser des virgules.
BASH $> IFS=$',' BASH $> vals='/mnt,/dev,/proc,/sys,/tmp,/usr/bin,/home/joda/Mes Documents' BASH $> for i in $vals; do echo $i; done /mnt /dev /proc /sys /tmp /usr/bin /home/joda/Mes Documents BASH $> unset IFS BASH $>
Il ne faudra cependant pas oublier les réinitialiser la valeur de la variable IFS avec la commande unset pour qu'elle retrouve sa valeur par défaut, sans quoi cela pourrait impacter le reste du script.