Cours ‐ Shell - vbridonneau/CoursSysteme GitHub Wiki

Shell

Le rôle du shell est de fournir une interface utilisateur afin de pouvoir lancer des commandes et naviguer dans le système de fichier.

Interface utilisateur

Différents types d'interpréteur

Il existe différents interpréteurs shell. Par défaut, les systèmes basés sur Linux et Unix utilisent bash, mais il en existe bien d'autres.

Nom Description
sh conforme au normes POSIX (donc portable)
bash surcouche de sh (configurable par exemple)
zsh shell du domaine public

Par la suite nous verrons comment exécuter et écrire des scripts et commandes en bash.

Interpréteur Bash

L'interpréteur bash permet d'écrire du code shell afin de lancer des commandes dans le terminal. Il est possible de le configurer afin de changer par exemple le prompt :

export PS1="\[\e[31m\][\[\e[m\]\[\e[38;5;172m\]\u\[\e[m\]@\[\e[38;5;153m\]\h\[\e[m\] \[\e[38;5;214m\]\W\[\e[m\]\[\e[31m\]]\[\e[m\]\\$ "

Le fichier .bashrc situé dans le répertoire ~/ permet de configurer l'interpréteur bash. Dans ce fichier on retrouve entre autre des variables d'environnements et des alias afin de configurer bash. Entre autres choses, on trouve des alias comme :

alias ls='ls --color=auto'

Les alias sont des raccourcis sur des commandes.

Combinaison de commande avec les pipes

Il est possible de rediriger la sortie d'une commande vers l'entrée d'une seconde commande.

commande1 | commande2

Un exemple d'utilisation serait de faire du traitement de texte. Par exemple, en supposant qu'un fichier contienne les informations suivantes :

nom,dateDeNaissance,villeDeNaissance
Alice,10/05/1997,Le Havre
Bob,11/06/1993,Rouen
...

Si l'on souhait extraire de ce fichier uniquement le nom des personnes ainsi que leur ville de naissance, on pourra écrire :

cat fichier | cut -d',' -f1,3

Redirections des flux d'entrée et de sortie

Comme tout programme C, un processus en cours d'exécution a une entrée standard ainsi qu'une sortie et erreur standard. Lorsque l'on souhaite mettre la sortie d'un programme lancé depuis le terminal, ou depuis un script shell, il suffit d'utiliser le symbol > :

commande > fichierSortie.txt

De la même façon que l'on peut renvoyer la sortie d'un programme dans un fichier, il est possible de renvoyer l'erreur standard ansi que l'entrée standard. Pour rediriger l'erreur standard dans un fichier, on utilisera la syntaxe 2>. Pour rediriger l'entrée standard vers l'entrée d'un programme, on utilisera le caractère <. Voici un exemple :

cat fichier | cut -d',' -f1,3 > donnee.csv 2> log.txt

Mettre des processus en arrière plan

Lorsque l'on souhaite exécuter des programmes que l'on sait mettre du temps à s'exécuter, il est possible de vouloir lancer le programme et de reprendre la main juste après. Pour cela, il suffit de rajouter le caractère & à la fin de la commande :

simulationTresLongue &

Un exemple reprenant les deux points précédents et ouvrant firefox depuis le terminal est le suivant :

firefox 2> /dev/null &

Variables

Les variables en bash servent à stocker des valeurs, soit des résultats de commandes ou des nombres. Pour créer une variables, on procède de la manière suivante :

MA_VARIABLE=valeur

Note : attention, il faut absolument que le symbole = soit collé au nom de la variable et à la valeur que l'on souhaite lui affecter.

Il est possible d'effectuer et de manipuler des variables shell.

Opérations arithmétiques

Pour effectuer des opérations arithmétiques sur des variables shelld, il suffit de les mettre à l'intérieur de $(()) ou $[]. Voici quelques exemples :

CPT=$((CPT + 1))
MULTIPLICATION=$[4 * 2]

Note : il est impossible de faire des divisions et d'obtenir des résultats sous forme de nombres flotants en bash avec l'opérateur /. Pour pallier le problème, on peut soit utiliser la commande bc ou d'utiliser la commande python3 -c.

Manipulation dans des chaines de caractères

Il est possible de manipuler des variables shell avec des chaines de caractères. Pour pouvoir utiliser une variable dans une chaine de caractère, on utilisera les " et pas les ' :

x=42
echo "Valeur de x : $x" # Affichera : Valeur de x : 42
echo 'Valeur de x : $x' # Affichera : Valeur de x : $x

Variables variables d'environnements

Les variables d'environnements sont des variables spéciales auxquelles on peut accéder depuis un script bash ou un programme C sans avoir à besoin de les définirs. Elles contiennent des valeurs de configurations déclarées dans des fichiers parcourus lors du lancement de bash ou lors de l'appel à la commande source sur un fichier contant des déclaration de telles variables. Elles sont accessibles via la commande env. Parmis elles on trouve :

Nom Description
USER nom de l'utilisateur
PATH ensemble de chemins de réperotires contenant des exécutables
LANG langue utilisée pour afficher des
PWD chemin absolu du répertoire courrant
HOME chemin absolu vers le home

Il est enfin possible de convertir une variable en variable d'environnement avec la commande export, comme dans l'exemple qui suit :

export LANG=fr_FR.utf8

Il est également possible de faire l'opération inverse :

export -n MA_VARIABLE

Si l'on souhaite accéder aux variables d'environnements, on peut y parvenir en utilisant la commande env. Elle affiche le nom des variables et leur valeur séparé par le symbol =:

(Prompt bash)$ > env
(Sortie sur le terminal):
SHELL=/bin/bash
WSL_DISTRO_NAME=Ubuntu-20.04
WT_SESSION=4e7b8725-3d72-40fc-9810-2634b20b0c23
NAME=DESKTOP-NA2NB2H
PWD=/mnt/c/Users/vincb/Documents/Systeme/WikiTPs/Bash
LOGNAME=vbridonneau
MOTD_SHOWN=update-motd
HOME=/home/vbridonneau
LANG=C.UTF-8

Scripts bash

Lorsque l'on souhaite écrire plusieurs commandes dans le but d'effectuer une tâche, il est utile de pouvoir les regrouper dans un même fichier appelé script. Dans ce qui suit, nous allons voir comment gérer les arguments d'un script, comment écrire des conditions et pour finir, comment écrire des boucles.

Paramètres

Comme pour des programmes C, il est possible de paramétrer des scripts bash :

bash monScript.sh arg1 arg2 ...

Pour accéder à la valeur des arguments dans un script, on utilise le symbole $ suivit de l'indice de l'argument. Un seul indice possède un statut à part, c'est $0. Il fait référence au nom du programme et peut être vu comme un équivalent de argv[0] en C. On pourra écrire le script suivant si l'on souhaite accéder au nom du script aisni qu'au neuf premiers arguments :

nomDuProgramme=$0
arg1=$1
arg2=$2
#...
arg9=$9

Attention : Même si on peut passer en argument plus de dix valeurs en argument, il n'est cependant pas possible d'accéder au dixième et au suivant en utilisant par exemple la syntaxe $10. Pour pouvoir y accéder, il faudra appeler la commande shift qui a pour effet de décaler les arguments de 1. Ainsi, après un appel à shift, $1 fera référence au second argument et $9 au dixième. Seul $0 n'est pas affecter par shift et stocke toujours le nom du script en cours d'exécution.

Voici un exemple manipulant les arguments d'un programme

programme=$0
arg1=$1

echo "Nom du programme : $programme, premier argument : $arg1"

En bonus, voici un exemple permettant d'obtenir le dixième argument avec shift :

# Un exemple d'utilisation de shift

echo "9ème argument $9"
shift
echo "10ème argument $9"

Conditions

Les conditions permettent d'exécuter des blocs de code en fonction d'une condition spécifique. Les structures de contrôle utilisées en bash sont if, elif et else. Elles s'utilisent comme suit :

if test1
then # Mot clé necessaire
    # Liste d'instructions
elif test2 # Equivalent à un else if
then
    # Liste d'instructions
else
    # Liste d'instructions
fi

Pour réaliser des tests de comparaison en bash, on utilise la syntaxe [ arg1 op arg2 ] ou [ op arg2 ]. Pour faire des comparaisons entre des nombres, les opérateurs sont les suivants :

Opérateur Description Exemple
-eq vérifie si deux nombres sont égaux [ $n -eq 0 ]
-ne vérifie si deux nombres sont différents [ $n -ne $m ]
-lt vérifie si le premier argument est strictement plus petit que le second [ $a -lt $b ]
-gt vérifie si le premier argument est strictement plus grand que le second [ $a -gt $b ]
-le vérifie si le premier argument est inférieur ou égal au second [ $a -le $b ]
-ge vérifie si le premier argument est supérieur ou égal au second [ $a -ge $b ]

On trouve également quelques opérations sur les fichiers :

Opérateur Description Exemple
-e vérifie si l'argument passé existe dans le répertoire courant [ -e $fichier ]
-f vérifie si l'argument passé est un fichier dans le répertoire courant [ -f $fichier ]
-d vérifie si un fichier est un répertoire [ -d $fichier ]

Et également des opérations sur les chaines de caractères :

Opérateur Description Exemple
= vérifie si deux chaines sont égales [ $s1 = $s2 ]
!= vérifie si deux chaines sont différents [ $s1 != $s2 ]
-z vérifie si une chaine de caractère est vide [ -z $chaine ]

Boucles

Les boucles permettent d'itérer sur une séquence d'instructions. En Bash, les boucles les plus couramment utilisées sont la boucle for et la boucle while.

La boucle for fonctionne comme suit :

for variable in commande
do
    # des choses à faire
done

Par exemple, si on veut parcourir tous les fichiers d'un répertoire, on peut écrire :

for fichier in `ls`
do
    # des choses à faire
done

La boucle while s'utilise d'une façon similaire :

while condition
do
    # Commandes
done

Voici l'exemple d'une boucle affichant les nombres de 0 à 10 et leur carré

for nombre in `seq 0 10`
do
    echo $nombre $[nombre*nombre]
done

Variables spéciales

Certaines métadonnées d'un script peuvent être accessibles comme par exemple :

Nom Description
$? statut renvoyé par la dernière commande exécutée
$@ liste des arguments d'un script
$* similaire à $@
$# nombre d'arguments passés à un script bash

Ces variables sont utiles à bien des usages. Avec elles, on peut par exemple vérifier si un script à bien reçu le bon nombre d'argument, ou si une commande c'est terminé sans problème.

programme=$0
arg1=$1

echo "Nom du programme : $programme, premier argument : $arg1"
echo "Nombre d'arguments : $#"
echo "Liste des arguments : $@"
echo "Premier argument : $arg1"

Voici un exemple reprenant tous les concepts que l'on a vu. Son but est de parcourir les fichiers d'un répertoire et d'afficher leur taille (la commande s'appelle du voir plus bas) :

if [ $# -ne 1 ]
then
    echo "Usage $0 [rep]"
    exit 1
elif [ -e $1 ] && ! [ -d $1 ]
then
    echo "$1 n'est pas un répertoire"
    exit 1
fi
REP=$1
for fichier in `ls $REP`
do
    if [ -f $fichier ]
    then
        du -h $fichier
    fi
done

Quelques commandes utiles

echo

La commande echo permet d'afficher du code dans le terminale. On peut l'utiliser par exemple lorsque l'on souhaite afficher la valeur d'une variable :

echo "Les chemins vers des exécutables sont : $PATH" # On fait bien attention à utiliser " et pas ' pour les chaines de caractères.

grep

La commande grep est utilisée pour rechercher des motifs dans des fichiers ou des flux de données. Voici un exemple simple permettant de rechercher dans un fichier C la liste de tous les includes réalisés :

grep "#include" < fichier.c

du

La commande du est utilisée pour afficher l'utilisation de l'espace disque par les fichiers et répertoires. Voici un exemple affichant la taille de chaque répertoire dans le répertoire courant de manière lisible pour un humain :

du -h

wc

La commande wc permet de compter le nombre de caractères, le nombre de mots et le nombre de lignes du fichier passer en paramètres. Par exemple, sur le fichier .bashrc on pourra écrire :

wc ~/.bashrc

On peut également combiner cette commande à d'autres. Par exemple, si l'on veut savoir combien de fichier commence par la lettre B ou b, on pourra écrire :

ls | grep "^[Bb]" | wc -l