Lazygeek Lazygeek
Title

Shebang

Ligne de commentaire à placer au tout début du fichier. Cette ligne indique à Linux le programme à utiliser pour exécuter le script.

#! /bin/bash 
  • /bin/bash : indique le chemin absolu vers le programme à utiliser
    • Nécessite que le programme soit réellement installé à cet endroit sur la machine cible
  • /usr/bin/env bash : Utilise les variables d’environnement pour déterminer automatiquement l’emplacement du programme indiqué. Cette solution est plus portable que la précédente.

Le shebang ne s’applique pas uniquement pour les scripts bash, mais également pour python ou tout autre script.

Modification du comportement du shell

Il est possible de modifier le comportement par défaut en positionnant certaines options à l’aide de la commande set.

  • set -e : exit 1 immédiatement en cas d’erreur (sauf dans les if, && et ||)
  • set -u : Retourne une erreur en cas de tentative d’accès à une variable indéfinie
  • set -v : Verbose
  • set -x : Affiche une trace de l’exécution de chaque commande (utile pour le debug)
  • set -o <option> : positionne l’option correspondante (certaines options possèdent un raccourci court)
    • pipefail : Renvoie une erreur si une commande dans un pipe renvoie une erreur (ce n’est pas le cas par défaut)

Plus d’options dans la documentation

Variables

Variables particulières

  • $# : nombre d’arguments du script ou de la fonction
  • $n : n-ième argument
    • $0 étant le nom du script
  • $@ : la liste de tous les arguments du script ou de la fonction sous forme de tableau
    • l’argument $0 (le nom du script) ne figure pas dans cette liste)
  • $? : code de retour de la dernière instruction exécutée
  • $$ : PID courant du script en cours
  • $! : PID du dernier process exécuté en background
  • $LINENO : numéro de ligne courant
  • $PATH : path courant

Opérations sur variables

  • $maVar : accès à une variable
  • ${maVar} : idem ci-dessus mais identifie clairement les limites du nom de la variable. Utile dans une utilisation type: echo "${n}th iteration"
  • ${maVar:-default} : Si la variable indiquée n’existe pas, utilise la valeur par défaut à la place
  • ${maVar:n:m} : retourne du n-ème au m-ème caractère de la chaine de caractère (ex: ${maVar:0:4} pour les 4 premiers caractères)
  • ${maVar:n} : retourne du n-ème caractère de la chaine jusqu’à la fin
  • ${maVar^} : retourne la variable mais avec la première lettre en majuscule
  • ${maVar^^} : retourne la variable mais avec tous les caractères en majuscules
  • ${maVar,} : retourne la variable mais avec la première lettre en minuscule
  • ${maVar,,} : retourne la variable mais avec tous les caractères en minuscules
  • ${maVar~} : retourne la variable mais en inversant la casse de la première lettre
  • ${maVar~~} : retourne la variable mais en inversant la casse de tous les caractères
  • ${maVar#pattern} : retourne la variable en supprimant la partie de la chaine qui match le pattern (?=1 unique caractère; *=nombre quelconque de caractères (y compris aucun))

    NOTE: la correspondance se fait uniquement du début de la chaine. NOTE: la correspondance se fait sur la partie la plus petit possible.

    • exemples (avec maVar=abcdef)
    • ${maVar#a} –> bcdef
    • ${maVar#a?} –> cdef
    • ${maVar#a*} –> bcdef (l’astérisque ne capture aucun caractère - correspondance la plus petite possible)
  • ${maVar##pattern} : idem ci-dessus mais avec la correspondance maximale
    • exemples (avec maVar=abcdef)
    • ${maVar##a*} –> <rien> (l’astérisque capture tous les caractères - correspondance maximale)
  • ${maVar%pattern} : idem # mais en partant de la fin de la chaine
  • ${maVar%%pattern} : idem ci-dessus mais avec la correspondance maximale

Tableaux & Dictionnaires

Tableaux

Déclarer un tableau

myList=(item1 item2 item3)

Affecter une valeur à une case du tableau

tab[0]='valeur'

Afficher une valeur du tableau

echo ${tab[0]}

Afficher toutes les valeurs du tableau

echo ${tab[@]}

Afficher toutes les clés du tableau

echo ${!tab[@]}

Dictionnaires

Déclarer un tableau comme dictionnaire

declare -A tab

Affecter une valeur à une case du tableau

tab['cle']='valeur'

Afficher une valeur du tableau

echo ${tab['cle']}

Afficher toutes les valeurs du tableau

echo ${tab[@]}

Afficher toutes les clés du tableau

echo ${!tab[@]}

Fonctions

myfunc()
{
  $# : nombres d'arguments
  $1 : 1er argument
}

myFunc arg1 arg2 ...

portée des variables

  • Par défaut une variable est globale
  • Si une variable est définie dans une fonction, elle sera globale mais ne sera définie et donc accessible qu’une fois la fonction appelée une 1ère fois
  • Pour définir une variable local à une fonction, il faut le faire explicitement avec le mot clé local

exit codes

  • dans les fonctions, avec le mot clé return
  • pour un script entier avec le mot clé exit
  • On récupère la valeur avec la variable $?
  • La variable $? contient le code d’erreur de la dernière fonction exécutée

Conditions

IF/THEN/ELSE

if [ $maVar -eq 2 ]    # attention aux espace autour des brackets
then
  ...
elif [ $mavar -eq 3] 
then
  ...
else
  ...
fi
if [ $maVar -eq 2 ]; then   # autre syntaxe, ne pas oublier le point-virgule
  ...
fi

Syntaxe particulière pour récupérer le code d’erreur d’une fonction sur une seule ligne.

ERROR=0
myFunc || ERROR=$?

Conditions disponibles

if [ -z $maVar ]   # si la variable est vide
if [ -n $maVar ]   # si la variable est non vide
if [ -e $fichier ]   # si le fichier/dossier existe
if [ -d $fichier ]   # si le fichier est un dossier
if [ -f $fichier ]   # si le fichier existe et est vraiment un fichier
if [ -s $fichier ]   # si le fichier existe et est non vide
if [ -L $fichier ]   # si le fichier est un lien
if [ -r/w/x $fichier ]   # si le fichier est accessible en lecture/écriture/exécution
if [ $fichier1 -nt/ot $fichier2 ]   # si le fichier1 est plus récent/ancien que fichier2 (nt: newer than / ot: older than)
if [ ! <condition> ]   # si la condition n'est pas remplie

Operateur ternaire

[ $a -eq 2 ] && funcIfTrue || funcIfFalse      # ici encore, attention aux espaces autour des brackets

FOR

for var in $liste
do
    ...
done

for i in {1..10}
do
    echo $i
done

WHILE

while <condition>
do
    ...
done

SWITCH/CASE

case $maVar in
    "cas1")
        <code>
        ;;
    "cas2")
        <code>
        ;;
    *)
        <default>
        ;;
esac

Parcourir un fichier

Option 1

Le fichier à lire est indiqué au début de la boucle while

cat file.txt | while IFS= read var1 var2 ; do
    echo $var1 $var2
done
  • IFS (optionnel) permet d’indiquer le séparateur à utiliser pour découper une ligne
  • Chaque morceau découpé de la ligne est affecté aux variables indiquées
    • Si plus de variables que de morceaux découpés → les variables restantes sont vides
    • Si plus de morceaux que de variables → La dernière variable contient le reste de la ligne, non découpé

Option 2

Le fichier à lire est indiqué à la fin de la boucle while

while IFS="/" read var1 var2 ; do
    echo $var1 $var2
done < file.txt

Variante: lire 2 fichiers à la fois (ou plus)

while read var1 && read var2 <&3 ; do
    echo $var1 $var2
done < file1.txt 3< file2.txt
  • La boucle s’arrête dès qu’un des 2 fichiers atteint la fin

Itérations

while <condition>; do
  <do some stuff>
done

operateur ternaire

[ $a -eq 2 ] && funcIfTrue || funcIfFalse      # ici encore, attention aux espaces autour des brackets

Here document

La syntaxe here document permet d’insérer un document dans un script bash ou directement dans un terminal. Typiquement du texte sur plusieurs lignes mais à traiter d’un seul bloc par une seule commande.

cat << EOF > file.txt
line 1
line 2
EOF

Tout le contenu à partir de la ligne suivante les << sera pris en compte par la commande indiquée (ici cat), jusqu’à rencontrer le mot-clé spécifié (ici EOF).

file1.txt

line1
line2

Notes:

  • Le mot-clé peut être remplacé par n’importe quel mot
  • Les variables sont remplacées par leur valeur et les commandes entre back-quotes sont exécutées
  • Si le mot clé est entre quotes ou précédé par un back-slash, les variables ne sont pas remplacées, les back-quotes ne sont pas exécutées

Faire des calculs

echo $((4+2))
> 6

a=3
echo $(($a+5))
> 8

i=$(($i+1))   # equivalent à i++

bc : calculatrice en ligne de commande

echo "3*50" | bc
> 150

a=50
echo "$a*3" | bc
> 150

a=`echo "3*12" | bc`
echo $a
> 36

Passer des options

Utiliser des options avec les scripts

./myScript.sh -h
./myScript.sh --help
./myScript.sh -f <option> -v

Options courtes

Si on ne souhaite utiliser que des options courtes, la commande getopts est suffisante.

  • Toutes les options disponibles collées
  • Les options nécessitant un paramètre immédiatement suivies d’un ‘:

Exemple pour un script avec les options suivantes:

  • -h pour l’aide
  • -f pour indiquer un nom de fichier. Le nom doit être indiqué juste après l’option
  • -v pour verbose
while getopts "hf:v" opt     # On parcourt toutes les options, en ayant pris soin de spécifier les options autorisées.
do
    case $opt in
        h)
            usage ;;
        f)
            filename=$OPTARG ;;    # Si l'option requiert un argument, celui-ci est dispo dans la variable $OPTARG
        v)
            VERBOSE=1 ;;
        *)
            usage ;;
    esac
done

Options longues

L’utilisation d’options longues (ex: –help) se fait à l’aide de la commande getopt

  • -o pour les options courtes
    • Toutes les options disponibles collées
    • Les options nécessitant un paramètre immédiatement suivies d’un ‘:
  • -l pour les options longues
    • Toutes les options dispo séparées par une virgule
    • Les options nécessitant un paramètre immédiatement suivies d’un ‘:

Exemple pour un script avec les options suivantes:

  • -h ou –help pour l’aide
  • -f ou –file pour indiquer un nom de fichier. Le nom doit être indiqué juste après l’option
  • -v pour verbose (pas d’option longue associée)
options=$(getopt -o hf:v -l help,file: -- "$@")    # Les options sont réarrangées. Les connues sont placée à gauche, puis un double -- et enfin la liste des options inconnues à droite. Les options combinées sont également séparées.
 
eval set -- "$options"   # Utilise la liste 'options' et la réinjecte en tant qu'argument du script. 
while true; do 
    case "$1" in     # L'option en cours d'analyse est en position 1
        -h|--help)
            usage      # On défini généralement une fonction usage() qui affiche les utilisations possibles du script
            break;;    # Instruction break qui permet de sortir de la boucle 'while true'
        -f|--file)
            fileName=$2    # L'argument placé après l'option est en position 2
            shift 2;;      # L'option et son argument ont été traités. On décale de 2 pour passer à la suite des options
        -v)
            VERBOSE=1
            shift;;      # On a mémorisé l'option -v dans une variable interne. On décale pour continuer à traiter les autres options.
        --)              # Fin des options connues. On interrompt la boucle (on peut continuer si on veut exploiter les autres options inconnues, mais il faut être sûr de quitter la boucle 'while true'.
            break;;
        *) usage      # Cas par défaut. On peut soit afficher un message d'erreur, soit rappeler le usage.
            break;;
    esac 
done

Exemple de fonction usage()

usage()
{
    echo "Usage: ./myScript.sh [options]" 
    echo "-h|--help                Display this help" 
    echo "-f|--file <filename>     Do whatever with the given file"
    echo "-v"                      Verbose"
}

Tips

  • Si l’ordre des options a de l’importance, il faut ajouter un tiret avant la première option courte.

    Exemple: getopt -o -hf:v