Le bash, programmation en GNU/Linux

LES moins jeunes d’entre nous auront connu un temps où l’ordinateur ne connaissait pas l’interface graphique (le bureau, avec ses icônes, ses fenêtres, ses menus…). Tout se passait alors au moyen de commandes saisies au clavier en regard d’une invite. Ce mode subsiste avec GNU/Linux ou tout autre Unix, ce qui permet de lancer une application avec des paramètres pointus ou de recevoir des messages (d’erreur) plus explicites. Le Bourne Again SHell (bash) permet de créer des scripts complexes.

Page du minimum à connaître sur un système GNU/Linux (ligne de commande, système de fichiers…)
Liste des commandes courantes à saisir dans une console
Commande concernant les fichiers et répertoires

1. Mode interactif

2. Nomenclature

2.1 Syntaxe
2.2 Opérateurs

3. Variables

3.1 Chaîne et affichage
3.2 Variables numériques
3.3 Tableaux
3.4 Passages de paramètres
3.5 Variables du système

4. Structures

4.1 Tests
4.2 if then else
4.3 Boucle while
4.4 Boucle until
4.5 Boucle for in
4.6 select in
4.7 case esac
4.8 Fonctions

5. Commandes

5.1 Système
5.2 Temps
5.3 Extractions
5.4 Mathématique
5.n …autres en vrac

9. Documentation

1. Mode interactif

Ce mode «console» ou «terminal» était utilisé avant le développement de l’interface graphique. Si pour une raison ou une autre, cette dernière ne peut être lancée, vous êtes en mode «ligne de commandes».

Si dans votre interface graphique vous ne trouvez pas de console, souvent représentée par un écran noir parmi les accessoires, frappez [alt-F2] et saisissez xterm, gnome-terminal sous GNOME, mate-terminal sous mate-desktop, ou konsole pour KDE.

nana@olympe:~$

Caractères de contrôle hérités du terminal DEC VT220

[Ctrl-a] se place au début de la ligne sans l’effacer, comme [Ctrl-flèche gauche]
[Ctrl-b] curseur un caractère vers la gauche, comme la flèche gauche
[Ctrl-c] interrompt une commande
[Ctrl-d] mange le caractère suivant, comme [Delete] ; ferme le terminal si la ligne de commande est vide
[Ctrl-e] saute à la fin de la ligne, comme [Ctrl-flèche droite]
[Ctrl-f] le curseur va un caractère plus loin
[Ctrl-h] le curseur mange un caractère à gauche, comme [Backspace]
[Ctrl-i] autocomplétion des commandes connues du système, comme [Tab]
[Ctrl-j] saute une ligne plus bas
[Ctrl-k] supprime les caractères à la droite du curseur
[Ctrl-l] saute un écran plus bas, en conservant la ligne de commande
[Ctrl-m] saute une ligne plus bas ([Enter])
[Ctrl-n] descend dans l’historique des commandes, comme [Flèche bas], voir [Ctrl-p]
[Ctrl-o] saute une ligne plus bas
[Ctrl-p] remonte dans l’historique des commandes, comme [Flèche haut], voir [Ctrl-o]
[Ctrl-q] reprend l’action d’une commande interrompue par [Ctrl-s]
[Ctrl-r] recherche dans l’historique des commandes
[Ctrl-s] suspend l’action d’une commande
[Ctrl-t] ramène l’avant dernier caractère après le dernier, le curseur avançant si cela est possible
[Ctrl-u] efface les caractères situés à sa gauche et se place au début
[Ctrl-w] coupe ce qui est à la gauche du curseur
[Ctrl-x][Ctrl-x] revient à une position antérieure du curseur (après s’être déplacé dans la ligne de commande)
[Ctrl-y] copie ce qui a été coupé par [Ctrl-w]
[Ctrl-z] interrompt certaines commandes

Paramétrage de la console

Le fichier caché .bashrc situé à la racine du répertoire personnel (/home/nana pour l’utilisatrice nana) contient un cetain nombre de paramètres qu’il est possible de modifier :

HISTSIZE=1000 fixe le nombre de commandes affichables par l’historique, qu'il est possible de rappeler avec la flèche «haut» ou par la commande history

HISTFILESIZE=2000 fixe le nombre de lignes de commandes conservées dans le fichier ~/.bash_history

HISTCONTROL="ignoredups ne garde pas dans l’historique la répétition de la commande précédente

HISTCONTROL="ignorespace ne garde pas dans l’historique une commande commençant par une espace

HISTCONTROL="ignoreboth résume les deux précédentes

HISTCONTROL="erasedups ne garde pas une même commande déjà dans l'historique

HISTCONTROL="erasedups;ignorespace" compose deux directives

HISTIGNORE="cd*;ls*" ne permet pas à l’historique de retenir les commandes cd et ls.

alias L="ls -l" permet de définir un raccourci

La console bash tiendra compte d’un nouveau paramétrage défini dans .bashrc après la commande

source ~/.bashrc

Il est possible de rergouper les aliases dans le fichier .bash_aliases, auquel cas il faut également utiliser la commande

source ~/.bash_aliases

De toute manière, le prochain «login» consultera le fichier .bash_aliases si le fichier .bashrc contient les lignes suivantes :

if [ -f ~/.bash_aliases ]; then
    . ~/.bash_aliases
fi

Note : le nombre de lignes affichables simultanément dans le terminal par une application est à paramétrer dans le terminal.

2. Nomenclature

Ce qui suit est valable en mode console ou en mode script.

2.1 Syntaxe

# permet d’écrire un commentaire dans le programme. De là le verbe "commenter" (ajouter un #) ou "décommenter" (enlever un #) pour rendre une ligne de script ineffective ou effective.

#! /bin/bash sur la première ligne du script appelle l’application bash pour un lancement automatique. Il faut également que le fichier qui contient le script soit rendu exécutable :

$ chmod 740 nom-du-script

Une expression non réservée est considérée comme une chaîne, jusqu’à l’espace suivant. Pour inclure un ou des espaces dans une chaîne, on utilise l’antislash \ suivi d’un espace, mais les guillemets sont plus indiqués. Voi également sortie de chaînes.

` ` entoure une commande passée au système. a=`ls -l` contient la liste des fichiers du répertoire courant

$ préfixe l’appel à la valeur d’une variable

{ } permettent des remplacements : echo a{pr,rm,r,i}e donne apre arme are aie

; sépare deux ou plusieurs commandes à enchaîner sur une même ligne

* représente un ensemble de caractères qui filtre les fichiers du répertoire courant (A* s’ils commencent par ’A’). \* permet de considérer le caractère * en tant que tel

? représente un caractère dans un filtre de fichiers du répertoire courant (?a* si la deuxième lettre est un ’a’). \? permet de considérer le caractère ? en tant que tel

& permet le lancement d’une commande dans un processus parallèle

&& sépare deux ou plusieurs commandes à enchaîner, mais toute erreur retournée par une commande ne permettra pas le lancement de la suivante.

|| sépare deux ou plusieurs commandes à enchaîner, mais seule l’erreur d’une commande permet le lancement de la suivante.

> fichier dirige le résultat d’une commande vers un fichier, créé pour l’occasion. S’il existe déjà, il est écrasé. C’est une manière très simple de créer un petit fichier texte :

echo "Un homme, une femme" > chabada.txt

Permet également de concaténer plusieurs fichiers :

cat texte1 texte2 > somme.txt

>> fichier ajoute le résultat d’une commande au bout d’un éventuel fichier, ou crée le fichier s’il est inexistant (alors équivalent à > fichier).

| (pipe) enchaîne les commandes où chacune reçoit les résultats de la précédente, permettant un traitement postérieur de la sortie d’une commande.

ls -l | sort listage de fichiers avec une mise en ordre alphabétique
ls -l | grep chaine listage avec un filtre : seuls les fichiers ou répertoires contenant chaine sont affichées

Il est possible d’utiliser more ou less en pipe : ls -l | less

Mots réservés

case))) esac
forin / do / done
function
ifthen / elif / else / fi
selectin … … … do / done
time
untildo / done
whiledo / done

2.2 Opérateurs

Disparition de la section?

3. Variables

3.1 Chaîne et affichage

Un nom de variable est définie par une chaîne, dont la valeur ne s’affiche qu’avec le préfixe $ :

q=cassis; echo $q # affiche cassis
q=cassis; echo q # affiche q

q est une variable contenant une chaine de six caractères. Si l’on veut inclure des espaces dans la chaîne, il faut utiliser des guillemets ou des échappements :

q="Va donc"; r="She’s like a rainbow"

Pour concaténer deux chaînes, ne pas utiliser le signe ’+’ :

a=aze; b=rty; c=$a$b; echo $c

Saisir une chaîne

La commande read permet de saisir une chaîne :

read variable attend une chaîne et [Enter] et la place dans la variable

read nom prenom saisit deux variables, les deux chaînes sont séparées par un espace, attention aux et "
-n 5 limite la saisie à 5 caractères, saisie automatique après 5 caractères
-p "Nom: " permet l’affichage d’une chaîne d’invite
-t 30 permet de fixer un nombre de secondes maximal pour la saisie
-s pour cacher la chaîne saisie (mot de passe)

S’il existe plus de chaînes séparées par un espace que de variables réceptrices, c’est la dernière qui ramasse le surplus.

read -n 1 -p "Une touche…"

permet de suspendre un script -n 1 accepte que n’importe quelle touche atteigne le nombre maximal de caractère(s), une variable de réception n’est pas nécessaire

Sortie de chaîne 2016.02

printf et echo -e permettent un affichage sur la sortie standard (a priori la console). Les variables n’affichent leur contenu que si elles sont incluses dans une chaîne entourée de guillemets doubles (sinon, c’est leur $nom qui apparaît).

’ ’ ne convient pas si

" " par contre

Note: echo -e "’$nom’" affiche le contenu de la variable nom entourée de deux guillemets simples.

Les caractères spéciaux sont interprétés si la chaîne est entourée de guillemets, simples ou doubles:

\a bip (selon la configuration de la console)
\b retour en arrière d’un caractère sans effacer : echo -e "aaa\b\bz" affiche aza
\c interrompt l’affichage de la chaîne (et le retour à la ligne de echo)
\e raccourci pour les séquences ECMA-48 (affichage ligne/colonne et couleurs) : printf "\033[…" s’abrège en printf "\e[…"
\f saut de page (en fait \v)
\n saut de ligne avec retour de chariot
\r retour au début de la ligne : echo -e "aaa\rz" affiche zaa
\t saut à la tabulation horizontale suivante (tous les 8 caractères)
\v tabulation verticale : même position une ligne plus bas
\\ affichage de l’antislash
\" permet l’affichage du guillemet double dans une chaîne définie par des guillemets double

Les deux premières séquences suivantes sont valables pour les octets de 32 (\040 ou \x20) à 255 (\377 ou \xFF) dans les tables de 256 caractères iso8859 ou windows-1252 ; les deux suivantes spécifiquement pour un codage unicode :

\nnn caractère exprimé en octal (chiffres de 0 à 7)
\xhh caractère exprimé en hexadécimal (chiffres de 0 à F)
\uhhhh caractère unicode exprimé en 4 chiffres hexadécimaux
\Uhhhh caractère unicode exprimé en 4 chiffres hexadécimaux

La différence entre printf et echo -e est que echo -e ajoute un retour à la ligne en fin de chaîne :

nana@olympe:~$ a=yes ; echo -e "$a\vno!"
yes
 no!
nana@olympe:~$
…mais pas printf :
nana@olympe:~$ a=yes ; printf "$a\vno!"
yes
 no!nana@olympe:~$

L’invite (prompt) nana@olympe:~$ qui suit printf n’est pas affichée à la ligne! Notons que echo -en "" a le même comportement que printf .

% n’est pas interprété de la même façon : il faut le doubler our l’afficher avec printf.

3.2 Variables numériques

L’utilisation de variables numériques nécessite un peu de subtilité.

q=43 est une affectation d’une chaîne de deux chiffres à la variable q. Pour exprimer la valeur de la variable ’q’, il faut préfixer cette dernière avec $ :

q=43 ; echo $q # affiche ’43’, la valeur numérique de ’q’
q=43 ; echo q # affiche la chaîne ’q’

Le bash n’acceptera un traitement numérique que si l’opération est précédée de let :

q=43; let z=q*2; echo $z # affiche 86 résultat de 43*2
q=43; z=q*2; echo $z # affiche la chaîne ’q*2’
q=43; z=$q*2; echo $z # affiche la chaîne ’43*2’ ($q ayant exprimé la valeur 43, traitée comme une chaîne)

La division / est une division entière, la mise en exposant est **
% ou mod est l’opération modulo : n % m retourne le reste de la division entière de n par m

+=, -=, *=, /=, %= (mais pas **=) permettent les expressions où le premier opérande reçoit le résultat de l’opération :

q=43; let q+=1; echo $q # incrémentation d’une unité : 43+1 = 44

Attention : les expressions ne supportent en général pas les espaces

nana@olympe~$ q= 43
bash: 43 : commande introuvable

3.3 Les tableaux

liste=(1 2 3 4 5 6 7 8 9 10) définit un tableau indexé (une liste de valeurs). On accède à chacune par un entier naturel, la première valeur étant désignée par le rang 0.

${liste[3]} est 4, la première valeur étant représentée par ${liste[0]}.

vlaams=(nul een twee drie vier vijf zes zeven acht negen tien)
read -p "Nombre de 0 à 10: " nummer
echo ${vlaams[nummer]}

Pour assigner une case particulière, on a recours à liste[15]=j, même si cette case n’a pas encore été définie et que les case 11, 12, 13 et 14 ne sont toujours pas définies.

echo ${!tabl[@]} renvoie une chaîne composée de tous les index définis
echo ${tabl[@]} renvoie une chaîne composée de toutes les valeurs du tableau

Les tableaux à deux dimensions en semblent pas possibles, mais il y a moyen de les simuler. Pour un damier de 10x10, soit un tableau à cent valeurs différentes, une coordonnée ne ligne / pe colonne est adressable par ${damier[(n-1)*10+p-1]}. Pour la 5e case de la 3e ligne : (3-1)x10+5-1 est l’index de la valeur dans le tableau.

 0 1 2 3 4 5 6 7 8 9
 10 11 12 13 14 15 16 17 18 19
 20 21 22 23 24 25 26 etc.

Tableaux associatifs

Un tableau associatif est une collection où les valeurs ne sont pas rangées dans une file et accessible par son index, mais par une clé. Deux clés différentes peuvent avoir la même valeur, mais une nouvelle valeur attribuée à une clé remplacera la valeur précédente.

declare -A compter # nécessaire pour qu’un tableau soit associatif
compter=([un]=ek [1]=ek [deux]=do [2]=do [trois]=tin [3]=tin [quatre]=char [4]=char [cinq]=panch [5]=panch)
echo ${compter[trois]} ${compter[4]}

Attention! Sans declare -A nomdutableau, il s’agira d’un tableau indexé, et une chaîne ne sera pas consédérée comme clé mais comme variable et son éventuelle valeur numérique considéré comme index (0 si sa valeur est une chaîne).

echo ${tabl[@]} renvoie une chaîne composée de toutes les valeurs du tableau
echo ${!tabl[@]} renvoie une chaîne composée de toutes les clés d’un tableau associatif

Les tableaux associatifs permettent également de simuler des tableaux multi-dimensionnels :

declare -A multi
multi[3,5]="trois fois cinq"
multi[5,3]="cinq fois trois"
a=3; b=5
echo ${multi[$a,$b]}
echo ${multi[$b,$a]}

3.4 Passages de paramètres

Lors de l’appel d’un script passé avec des paramètres, les variables $1, $2… récupèrent les valeurs des valeurs passées lors de la saisie du fichier bash,$# le nombre de paramètres passés. Pour le fichier essai.bash :

#! /bin/bash
echo "$# $1 $2 $3"

La saisie de ./essai.bash a b zy 12 retournera la ligne

./essai.bash 4 a b zy

Le nombre de paramètres passé est de 4 : a, b, zy et 12, même si seulement 3 variables étaient prévues pour les recevoir.

$@ retournent une chaîne reconstituée de tous les paramètres passés avec la commande, séparés d’un espace : ’$1 $2 $3 …’

$* retournent une chaîne reconstituée de tous les paramètres passés, séparés par le caractère contenu dans la variable système $IFS, en général un espace.

shift permet de décaler les variables *1,*2… d’une variable à droite. C’est intéressant à utiliser dans une boucle jusqu’à épuisement des paramètres :

until [[ -z $1 ]] # tant que $1 n’est pas une chaîne vide
do
echo $1 # affiche le paramètre
shift # décale $1 d’un paramètre vers la droite
done

$0 représente la saisie du fichier bash (éventuel chemin plus nom du fichier)
$_ dernière commande exécutée (pas les structures ni echo)
$$ identificateur du processus en cours

$#, $@ et $* varient avec le décalage dû à shift.

3.5 Variables du système

(liste non exhaustive)

$PWD contenant l’adresse absolue du répertoire actuel
$DIRSTACK : le répertoire actuel (sans le chemin)
$OLDPWD contient celle du répertoire précédent.
$HOME contient l’adresse du répertoire de base de l’utilisateur (/home/toto).

$BASH_VERSION ’4.2.37(1)-release’ version du bash
$MACHTYPE ’x86_64-pc-linux-gnu’ architecture
$HOSTTYPE ’x86_64’ type de la machine
$OSTYPE : ’linux-gnu’

Le tableau BASH_VERSINFO contient également ces informations :

${BASH_VERSINFO[0]} retourne 4 : numéro majeur de la version
${BASH_VERSINFO[1]} retourne 2 : numéro mineur de la version
${BASH_VERSINFO[2]} retourne 37 : numéro de correction de patch
${BASH_VERSINFO[3]} retourne 1 : ?
${BASH_VERSINFO[4]} retourne ’release’
${BASH_VERSINFO[5]} retourne ’x86_64-pc-linux-gnu’ : architecture

$UID le numéro de l’utilisateur (501, 1000…)
$LOGNAME nom de l’utilisateur
$EUID le numéro de l’utilisateur effectif (501, 1000…), la même chose la plupart du temps
$GROUPS numéro de groupe à qui appartient l’utilisateur ???
$HOSTNAME ’olympe’ nom de la machine

$IFS séparateur de champ (nouvelle ligne, tabulation, espace…)
$PATH /usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games les adresse des binaires
$PS1 à PS4 les différentes définitions de l’invite
$SECONDS nombre entier de secondes du déroulement du script ou de l’ouverture du terminal
$RANDOM renvoie un entier positif aléatoire < 32768. let "a=$RANDOM%6+1" ($RANDOM modulo 6 renvoie les nombres de 0 à 5 inclus) simule le jet d’un dé. Voir l’exemple dans une fonction.

$COLORTERM nom du terminal, de la console
$LANG la locale installée, par exemple fr_FR.UTF-8 ou fr_BE.UTF-8

4. Structures

Certaines structures contiennent un test.

4.1 Tests

Ces tests sont utilisés dans les structures IF, WHILE et UNTIL.

Tests sur les chaînes

Comme une valeur est a priori une chaîne, nous commencerons par les tests réalisés sur les chaînes.

[ -n $chaine ] vérifie que la chaine n’est pas vide
[ -z $chaine ] vérifie que la chaine est vide ("" ou ’’)

[ $ch1 = $ch2 ] teste l’égalité entre deux variables chaînes
[ $ch1 == $ch2 ] est un équivalent de l’expression précédente
[ $ch1 != $ch2 ] teste l’inégalité entre deux variables chaînes

[ $ch1 "<" $ch2 ] teste l’antériorité de la première chaîne (ordre alphabétique)
[ $ch1 ">" $ch2 ] teste la postériorité de la seconde chaîne (ordre alphabétique)

Les guillemets sont nécessaires pour éviter la confusion avec les redirections de données. Attention : les lettres accentuées viennent après l’alphabet : a…z, é, ù…

Tests sur les nombres

Rappel : une variable numérique est initialisée avec l’expression let "var=43"

[ $a -eq $b ] (equal) teste l’égalité entre deux nombres (ou variables numériques)
[ $a -ne $b ] (non equal) teste l’inégalité entre deux nombres
[ $a -lt $b ] (lesser than) teste si le premier nombre est plus petit que le second
[ $a -gt $b ] (greater than) teste si le premier nombre est plus petit que le second
[ $a -le $b ] (lesser or equal) teste si le premier nombre est plus petit ou égal au second
[ $a -ge $b ] (greater or equal) teste si le premier nombre est plus grand ou égal au second

Tests sur les fichiers

[ -e $nom ] (exist) vérifie l’existence d’un fichier ou répertoire

[ -d $nom ] (directory) vérifie que le nom est celui d’un répertoire
[ -f $nom ] (file) vérifie que le nom est celui d’un fichier
[ -L $nom ] (Link) vérifie que le nom est celui d’un lien symbolique

[ -r $nom ] (readable) vérifie que le fichier est lisible (r ou 4).
[ -w $nom ] (writable) vérifie que le fichier est modifiable (w ou 2)
[ -x $nom ] (executable) vérifie que le fichier est exécutable (x ou 1)

[ nom1 -nt $nom2 ] (newer than) vérifie que le premier fichier est plus récent que le second
[ nom1 -ot $nom2 ] (older than) vérifie que le premier fichier est plus ancien que le second

Expressions régulières

Une expression régulière est une chaîne contenant une série de règles valables décrivant une série de chaînes. Attention : le test est placé entre [[ ]], la chaîne à tester ou la $variable est à gauche et l’expression régulière est à droite.

L’égalité =~ permet la recherche d’un pattern de chaîne :

[[ $ch =~ id ]] teste si la chaine contient id
[[ $ch =~ ^di ]] teste si di se trouve au ^début de la chaine
[[ $ch =~ rac$ ]] teste si rac est situé à la fin$ de la chaine
[[ $ch =~ [Cc]lou ]] teste si Clou ou clou se trouve dans la chaîne : [Cc] permet le choix entre C et c
[[ $ch =~ d[ar-t]i ]] teste si dai, dri, dsi ou dti se trouve dans la chaîne. [r-t] signifie les lettres entre r et t
[[ $ch =~ d{2} ]] teste si une chaîne de deux d est contenue dans la chaîne
[[ $ch =~ d{2,5} ]] teste si une suite de 2 à 5 d est contenue dans la chaîne
[[ $ch =~ in|du ]] permet le choix entre les chaînes in et du pour tester
[[ $ch =~ di?n ]] la chaîne doit conternir dn ou din; possibilité de i entre d et n
[[ $ch =~ di+n ]] au moins un i entre d et n dans la chaîne : din, diin, diiin
[[ $ch =~ di*n ]] pas de i, un ou plusieurs i entre d et n : dn, din, diin, diiin
[[ $ch =~ (ah){3} ]] teste l’existence de ahahah dans la chaîne
[[ $ch =~ \* ]] pour tester un *, l’expression régulière doit échapper son *

L’égalité == permet des tests d’égalité plus classiques utilisant les jokers. ? vaut n’importe quel signe et * une séquence de plusieurs signes :

[[ $ch == d?n ]] teste l’égalité avec une chaîne de trois lettres commençant par d et terminant par n
[[ $ch == d*n ]] teste l’égalité avec une chaîne de longueur indéterminée commençant par d et se terminant par n

4.2 condition «if then else»

Exécute la ou les commande(s) situées entre then et fi si le test condition(s) s’avère exact. Attention! la syntaxe de bash est assez capricieuse : l’espace est nécessaire entre les parenthèses carrées et l’expression de la condition.

if [ condition(s) ]
then
 commande(s)
fi

Des commandes alternatives peuvent être lancées si la condition n’est pas respectée : elles se placent entre else et fi.

if [ condition(s) ]
then
 commande(s)
else
 autres commande(s)
fi

Il est possible d’imbriquer des conditions :

read -p "Deux nombres séparés par un espace : " n1 n2

if [ $n1 -eq $n2 ]
then
 echo "Les deux nombres sont égaux"
else
 if [ $n1 -lt $n2 ]
 then
 echo "$n1 est plus petit que $n2"
 else
 echo "$n1 est plus grand que $n2"
 fi
fi

Les indentations ne sont pas nécessaires, mais permettent de structurer visuellement le script. Il est encore possible d’utiliser elif, qui peut préciser une condition alternative :

read -p "Deux nombres séparés par un espace : " n1 n2
if [ $n1 -lt $n2 ]
then
 echo "$n1 est plus petit que $n2"
elif [ $n1 -gt $n2 ]
then
 echo "$n1 est plus grand que $n2"
else
 echo "Les deux nombres sont égaux"
fi

Il est permis de condenser l’écriture, au risque de perdre la vision de la structure :

read -p "Deux nombres séparés par un espace : " n1 n2
if [ $n1 -lt $n2 ]
 then echo "$n1 est plus petit que $n2"
elif [ $n1 -gt $n2 ]
 then echo "$n1 est plus grand que $n2"
else echo "Les deux nombres sont égaux"
fi

Et même d’en arriver à ceci (ne pas oublier les ; entre les conditions et les then, et avant le fi) :

read -p "Deux nombres séparés par un espace : " n1 n2
if [ $n1 -lt $n2 ]; then echo "$n1 est plus petit que $n2"
elif [ $n1 -gt $n2 ]; then echo "$n1 est plus grand que $n2"
else echo "Les deux nombres sont égaux"; fi

Un bon compromis serait de séparer structure/conditions et commandes sur des lignes différentes :

read -p "Deux nombres séparés par un espace : " n1 n2
if [ $n1 -lt $n2 ] ; then
 echo "$n1 est plus petit que $n2"
elif [ $n1 -gt $n2 ] ; then
 echo "$n1 est plus grand que $n2"
else
 echo "Les deux nombres sont égaux"
fi

4.3 Boucle «while»

Il s’agit d’une structure dans laquelle le script reste tant que la condition d’entrée est maintenue.

while [ condition(s) ]
do
 commande(s)
done

Elle peut servir à fabriquer un compteur. Le script ci-dessous peut s’interpréter de la sorte : «une variable numérique est initialisée à 0 ; tant qu’elle reste inférieure à 10, elle est affichée puis incrémentée d’une unité».

let "i=0"
while [ $i -lt 10 ]
do
 echo "$i"
 ((i++))
done

Souvent rencontrés dans un test :

Pour traiter un fichier-texte ligne par ligne :

#! /bin/bash
while read line
  do
    echo $line
  done < monfichier

Pour traiter un fichier-texte mot par mot, voir la boucle for.

4.4 Boucle «until»

Il s’agit d’une structure dans laquelle le script reste jusqu’à ce que la condition d’entrée soit vraie.

until [ condition ]
do
 commande(s)
done

Cette boucle peut également servir à fabriquer un compteur. Le script ci-dessous peut s’interpréter de la sorte : «une variable numérique est initialisée à 0; jusqu’à ce qu’elle égale 10, elle est affichée puis incrémentée d’une unité; le code s’interrompt alors.

let "i=0"
until [ $i -eq 10 ]
do
 echo "$i"
 ((i++))
done

break permet de quitter la boucle autrement que par la condition d’entrée de boucle.
continue permet de repasser directement en début de boucle.

Ces deux instructions peuvent être liées à un test.

4.5 boucle «for»

Il ne s’agit pas de la boucle du compteur comme en BASIC ou C, mais d’une structure dans laquelle la ou les commande(s) sont exécutées pour chaque élément d’une liste.

for element in element1 element2 element3 element4 element5
do
 commande(s)
done

S’il n’y a pas de liste d’éléments, la variable parcourt la liste des variables $1, $2… issue des paramètres de l’appel du script.

for var
do
 commande(s)
done

break permet de quitter la boucle autrement qu’à la fin du parcours de la liste.
continue permet de repasser directement en début de boucle, et donc de passer à la valeur suivante de la liste parcourue.

Ces deux instructions dépendent souvent d’un test.

Cette boucle peut être utilisée pour recourir les fichiers du répertoire courant :

for i in *.wav
  do
    echo $i
  done

…ou tous les mots d’un fichier-texte (les espaces et les fins de lignes sont considérées comme des séparateurs) :

for mot in `cat monfichier`
  do
    echo mot
  done

Pour traiter un fichier-texte ligne après ligne, voir la boucle while.

4.6 boucle «select»

Il s’agit d’une structure qui ressemble à la boucle for et permet un choix sous forme de menu :

PS3="Invite: "
select variable in proposition1 proposition2 proposition3
do
 commande(s)
done

affichera :

1) proposition1
2) proposition2
3) proposition3
Invite:

L’exemple suivant permet la sortie d’un des mots ’alpha’, ’beta’, ’gamma’ ou ’chabada’ en saisissant respectivement 1, 2, 3 ou 4. PS3="" permet de définir l’invite prenant place après l’énoncé des quatres mots. La condition IF/THEN/ELSE/FI se borne ici à écrire le mot ou sortir avec un autre nombre que de 1 à 4 ; une saisie vide réaffiche le menu.

PS3="Choisir un mots par son numéro: "
select mot in alpha beta gamma chabada; do
 if [[ -n $mot ]]; then
 echo $mot
 else
 echo ’Mauvais choix!’
 break
 fi
done

select fichier in $@ permet un choix entre les paramètres du script, et select fichier in * un affichage de tous les fichiers du répertoire courant, contenu dans $PWD.

4.7 case esac

Cette structure permet un choix où les cas sont prédéfinis. La première ligne contient la variable dont on teste l’égalité avec les valeurs à tester, situées avant le signe ) . Les cas sont séparés entre eux par un double ;; . En dernier lieu, *) règle les cas non définis, similaire à else de la structure if then fi.

let "a=$RANDOM%6 + 1"

case $a in
1) msg="un" ;;
2) msg="deux" ;;
3) msg="trois" ;;
*) msg="autre" ;;
esac

echo "$a: $msg"

Toute la séquence aurait pu s’écrire :

let "a=$RANDOM%6 + 1"
case $a in 1) msg="un" ;; 2) msg="deux" ;; 3) msg="trois" ;; *) msg="autre" ;; esac
echo "$a: $msg"

Il est possible de prévoir des expressions régulières :

let "a=$RANDOM%100"

case $a in
[1-7]) msg="’[1-7]’ qui signifie de 1 à 7"
 ;;
[89]|1[0-9]) msg="’[89]|1[0-9]’ qui signifie de 8 à 9 ou 1 suivi de 0 à 9"
 ;;
2[0-9]) msg="’2[0-9]’ qui signifie 2 suivi de 0 à 9"
 ;;
[3-7][0-9]) msg="’[3-7][0-9]’ qui signifie de 3 à 7 suivi de 0 à 9"
 ;;
*) msg="’*’ qui signifie tout ce qui n’a pas été prévu"
 ;;
esac

echo "Le nombre $a est capté par l’expression régulière $msg"

4.8 fonctions

Il s’agit d’un bout de code séparé, que l’on peut invoquer de n’importe quel autre endroit du script, à condition que la définition de la fonction se trouve avant l’appel.

function nom_de_fonction {
commande(s)
}
nom_de_fonction

Il est possible de les définir autrement :

nom_de_fonction() {
commande(s)
}

Les valeurs suivant le nom de la fonction sont autant de paramètres, récupérés pour la fonction par $1, $2, $3…

function exposant {
 echo $1 $2
 let "c=$1**$2"
}

exposant 3 4
echo $c

Les variables étant globales, une variable définie dans une fonction correspond à celle utilisée ailleurs. Il est possible de définir des variables locales (dont les valeurs n’influencent pas les variables de même nom en dehors de la fonction) avec local variable

function deuxdes {
 # simule le lancer de deux dés ($RANDOM%6 rend de 0 à 5)
 let "des = $RANDOM%6 + $RANDOM%6 + 2"
}

deuxdes
echo $des

La valeur (de 0 à 255, qui sert de code) renvoyée par un éventuel return est récupérée par la variable $? et librement interprété par le script.

5. Commandes

Beaucoup de commandes en relation avec le bash restent à présenter ; certaines sont présentées sur cette page.

5.0 help

help commande donne une information sur ladite commande qui se trouve ci-après (pour le bash en général, voir info bashman commande pour d’autres).

. — : — (( expression )) — [ arg… ] — [[ expression ]] — alias — bg — bind — break — builtin — caller — case in — cd — command — compgen — complete — compopt — continue — coproc — declare — dirs — disown — do done — echo — enable — eval — exec — exit — export — false — fc — fg — for — fordo done — function — getopts — hash — history — if then elif — jobs — job_spec — kill — let — local — logout — mapfile — popd — printf — pushd — pwd — read — readarray — readonly — return — select in do done — set — shift — shopt — source — suspend — test — time — times — trap — true — type — typeset — ulimit — umask — unalias — unset — until — wait — while

5.1 Système 2023.12

export liste les valeurs des variables du système. Parmi elles :

LANG="fr_BE.UTF-8"
LANGUAGE="fr_BE:fr"
SHELL="/bin/bash"
USER="toto"
LOGNAME="toto"
HOME="/home/toto"
OLDPWD="/home/toto/Bureau"
PWD="/home/toto/afaire"
PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games"
GDMSESSION="lightdm-xsession"
XDG_CURRENT_DESKTOP="MATE"
XDG_SESSION_DESKTOP="lightdm-xsession"
DESKTOP_SESSION="lightdm-xsession"
DISPLAY=":0"

PATH contient les répertoires où sont situés les commandes et exécutables appelables

export PWD="/home/toto/MesDocs" ; cd $PWD fixe le répertoire courant.

5.2 Temps 2023.12

date retourne le jour, la date, l’heure et le décalage au temps universel coordonné de la machine et permet de les fixer.

$ date
mer 20 déc 2023 18:09:42 CET

#En mode super-utilisateur: su - [Enter] date -s "2012-01-27 13:14:56" met le système à l’heure

date -d @1234567890 renvoie la date contenant la seconde 1234567890 depuis le 1er janvier 1970 à 0h00:00

Pour modifier le fuseau horaire en Debian (l’interface graphique devrait pouvoir le faire)

#En mode super-utilisateur: su - [Enter]
+ mot de passe super-utilisateur
dpkg-reconfigure tzdata

touch fichier actualise la date et l’heure d’un fichier existant.

time commande ou scriptrenvoie le temps d’exécution d’une commande, script ou application après leur fermeture. time ls -l par exemple renvoie après la liste des fichiers du répertoire courant :

real    0m0,008s
user    0m0,004s
sys     0m0,004s

sleep n attend n secondes (par défaut) ou 3m, 2h ou 5d ; les nombres peuvent être non entiers : 1.6m ou .7h ; plusieurs arguments s’additionnent :

$ time ls -l
  ...liste)...
real    0m0,008s
user    0m0,004s
sys     0m0,004s

5.3 Extractions 2016.02

grep

grep pattern fichier(s) permet l’extraction de lignes (délimitées par l’octet 10 \n) d’un ou plusieurs fichier(s). Le pattern est interprété comme une expression régulière, dont vous trouverez des éléments d’explication ici ou .

toto@localhost:~$ grep toto /etc/group
cdrom:x:24:toto
audio:x:29:toto,pulse
  …
video:x:44:toto
toto:x:1000:
scanner:x:115:saned,toto

retourne les groupes auxquels l’utilisateur toto est affilié. Quelques options :

-v sélectionne les lignes où le pattern n’est pas retrouvé
-i précise que la casse est ignorée, dans le pattern comme dans le(s) fichier(s) cible(s)
-f permet de préciser un fichier où plusieurs patterns ont été définis, un par ligne
-F précise que le pattern est une chaîne de caractères fixes (non des expressions régulières)

-E («extended») et -P (Perl) offrent semble-t-il peu de différence en UNIX, ainsi que les commandes egrep (grep -E …) et fgrep (grep -F …). Voyez man grep ou info grep pour plus infos et d’options.

zgrep, zegrep, zfgrep, bzgrep, bzegrep, bzfgrep permettent la recherche dans des fichiers compressés .zip et .bz.

grep agit également sur le flux issu d’une autre commande :

ls -l | grep .txt
cat fichier | grep amarcor

awk

awk (paquet gawk ou original-awk) est un éditeur de base de données en fichiers de simple texte, dont les lignes représentent les observations, les données étant séparées par une tabulation.

toto  25  132  Kza  Tati
nana4584KzaAllen
pep27115HbEtaix

Par exemple, pour trouver les fichiers du répertoire courant pesant moins de 400 octets, il faut filtrer la commande ls -l

-rw-r--r--  1 jc   jc         72 avr 22 18:06  essai.py
-rw-r--r--  1 jc   jc        196 avr 15 18:16  junk

avec la commande awk qui impose une condition (le cinquième champ doit être un nombre de moins de 400 octets) pour afficher le cinquième champ ainsi que le neuvième, qui représente le nom de fichier :

ls -l | awk ’$5 < 400 { print $5 "\t" $9 }’

Attention : le code qui suit awk doit être écrit entre ’guillemets simple’, et la chaîne entre $5 et 59 entre "guillemets doubles" ; $0 pour la ligne entière. Voir également man akw

5.3 Mathématiques

false + true

Il ne s’agit pas de variables, mais de commandes retournant une valeur.

false simule une commande qui s’interrompt, ne permettant pas dans ce cas le lancement de la suivante :

false && echo yes

true simule une commande qui se termine bien et permet la suite de la ligne dans cet exemple :

true && echo yes
yes

C’est l’inverse avec ||

false || echo yes
yes

true simule une commande qui se termine bien et permet la suite de la ligne dans cet exemple :

true || echo yes

Évidemment, les valeurs true et false sont souvent issus d’une évaluation [si un répertoire de nom gnu existe] :

nana@olympe:~/$ [ -d gnu ] && cd gnu
nana@olympe:~/gnu$

factor

factor nnn factorise les entiers jusqu’à 2**64-1

bc

bc lance une calculette scientifique, en fait un prépocesseur de dc, calculatrice en notation polonaise inverse (RPN)

+ - * / % ^ ( ) ++ -- s’organisent entre eux de façon habituelle ; un retour-chariot valide le calcul

< > <= >= == != retournent 1 si la relation entre deux valeurs est vraie
&& || sont le «et» et le «ou» logiques

; entre chaque calcul, # pour un commentaire jusqu’en fin de ligne; /* pour une utilisation plus souple, même à l’intérieur d’une expression */ :

5+ /* ah! */ 3
8

last contient la valeur de la dernière sortie.
length() retourne le nombre de chiffres du résultat d’une expression.

var= permet d’assigner un nombre à une variable. Le nom commence par une minuscule, à laquelle on peut adjoindre d’autres minuscules, tiret_bas et chiffres

ibase= impose une base pour les nombres saisis, obase= pour les nombres retournés par l’application. Les chiffres de 10 à 15 sont A, B, C, D, E, F, nécessairement en majuscules. Les modifications doivent évidemment être prises en compte pour un changement de base ultérieur!

last contient la valeur de la dernière sortie.

a=read() attend une saisie

bc -l permet le mode virgule flottante.

s() pour sin - c() pour cos - a() pour atn - l() pour log - e() pour exp - sqrt()

scale= définit la précision, nombre de chiffres après la virgule
scale() retourne le nombre de décimales du résultat d’une expression

quit pour quitter l’application

Peut s’utiliser en pipe : echo "(5-2)*(5+2)" | bc

5.n Autres commandes

alias ll="ls -l" permet de remplacer une commande complexe par une simple
unalias ll défait le lien entre l’alias et toute autre commande

Cette commande ne dure que pour la session bash sur un terminal. Pour le pérenniser, l’inscrire dans le fichier .bash_aliases à la racine de l’espace utilisateur, éventuellement à créer (le point initial précise qu’il s’agit d’un fichier caché), puis appliquer :

source ~/.bash_aliases

Si un alias a exactement le même nom d’une une commande qu’il remplace, par exemple alias ls="ls -lahs", il suffit, pour lancer la commande initiale, de la préfixer de \ :

\ls

Droits

su, login, logout, chgrp, chmod, chown - voir basiques

Montage

mount, mountpoint, umount, dd, sync, findmnt, fusermount monte les système FUSE - voir partitions

NTFS : ntfs-3g, ntfs-3g.probe, ntfs-3g.secaudit, ntfs-3g.usermap, ntfscat, ntfsck, ntfscluster, ntfscmp, ntfsdump_logfile, ntfsfix, ntfsinfo, ntfsls, ntfsmftalloc, ntfsmove, ntfstruncate, ntfswipe

Archivage et compression

bzip, cpio, tar, gzip, bzip2, bzip2recover, gunzip, bunzip2, uncompress, zforce, znew, gzexe, bzexe - voir archivage.

Éditeurs

ed, red, sed, nano, rnano n’agit que sur un fichier inclus dans la ligne de commande

Système

uname -a renvoie toutes sortes de paramètres : Linux olympe 3.9-1-amd64 #1 SMP Debian 3.9.8-1 x86_64 GNU/Linux

lsblk (list blocks)renvoie l’organisation des disques

df (disk free) renvoie les statistiques d’utilisation des disques

dmesg (root!) renvoie les avis de mise en route de la machine et des connexions du hardware.

lsmod (list module) renvoie la liste et les dépendances des modules

Processus

open, openvt, ps, kill, wait,

Réseau

nc, nc.traditional ping, ping6, ss, netcat, netstat, nisdomainname, ypdomainname, dnsdomainname, domainname, hostname, ip

À classer

busybox, chacl, chvt, dash, dumpkeys, fgconsole, fuser, getfacl, kbd_mode, kmod, lessecho, lessfile, lesskey, lesspipe, loadkeys, lowntfs-3g, mknod, mktemp, mt, mt-gnu, pidof, mrbash, readlink, run-parts, setfacl, setfont, setupcon, sh, sh.distrib, stty, tempfile, ulockmgr_server, unicode_start, usb_printerid, vdir, vmmouse_detect, which

set, test, unset, bg, bind, builtin, command, declare, dirs, disown, enable, eval, exec, exit pour quitter un script exit + nombre permet un statut d’erreur, export, fc, fg, getopts, hash, help, history, jobs, local, popd, pushd, readonly, select, source, suspend, times, trap, type, typeset, ulimit, umask

9. Documentation

www.gnu.org/software/bash/manual/bashref.html
www.tldp.org/LDP/abs/html/ - The Linux Documentation Project
www.linuxtopia.org/online_books/advanced_bash_scripting_guide
www.linuxcommand.org
www.freeos.com/guides/lsst

En français

fr.openclassrooms.com/informatique/cours/reprenez-le-controle-a-l-aide-de-linux