Les base du SHell

Sommaire

Index

Variables

Affectation

reponse=42

Substition (utilisation de la valeur)

On utilise le caractère $

echo $reponse

Demander une valeur à l’utilisateur

read nom_variable

L’utilisateur doit saisir une chaîne de caractères et terminer par la touche Entrée

Affichage:

echo $nom_variable

Demande avec message:

$ read -p "saisir une valeur: " valeur
saisir une valeur: hello 
$ echo $valeur
hello

Faire les exercices 1 et 2

Tests et contrôle du code

Exécution conditionnelle

Pour vérifier une condition avant d’exécuter une commande, on utilise la structure syntaxique suivante:

if CONDITION
then
    COMMANDE
fi

Vérification si une commande s’exécute correctement

#/bin/bash

if grep laurent /etc/passwd
then
    echo OK

fi

Exemple: existence d’un fichier

fichier=/tmp

if [[ -e $fichier ]]
then
    echo "le fichier existe"
fi

Exemple: négation logique (non existence d’un fichier)

fichier=/tmp

if [[ ! -e $fichier ]]
then
    echo "Le fichier n'existe pas"
fi

Exécution conditionnelle avec alternative

if CONDITION
then
    COMMANDE_SI_CONDITION_VRAIE
else
    COMMANDE_SI_CONDITION_FAUSSE
fi

Exemples

Vérification si une commande s’exécute correctement

#/bin/bash

if grep laurent /etc/passwd
then
    echo OK
else
    echo KO
fi

Vérification si un fichier existe

fichier=/tmp/nexistepas

if [[ -e $fichier ]]
then
    echo "le fichier existe"
else
    echo "Le fichier n'existe pas"
fi

Tests sur les fichiers

La commande help test donne tous les opérateurs utilisables dans une condition de type [[ ... ]]

Faire les exercices 3 et 4

Caractères spéciaux

Backslash \

Commande sur ligne multiple

$ echo Des chose \
> sur plusieurs lignes
Des chose sur plusieurs lignes

Commentaire #

# Ceci est un commentaire
echo "Message" # Ceci est aussi un commentaire

Dans une chaîne de commandes

cat /etc/passwd |\
# Redirige cat vers grep
grep www-data

Guillemets

Double quote "

Citation partielle (ie interprétée)

$ echo "$reponse=.*"
42.*

Single quote '

Citation complète (non interprétée)

$ echo '$reponse=.*'
$reponse=.*

Interprétation des caractères

$ echo $'\n\nJe suis la dernière ligne'


Je suis la dernière ligne
$ echo '\n\nJe suis la dernière ligne'

Séparateur de commande

point-virgule ;

Permet de mettre plusieurs commandes sur une même ligne

echo salut ; echo les gens
$ echo bonjour ; echo salut
bonjour
salut
$ for i in 1 2 3 ; do echo $i ; done
1
2
3
$ for i in 1 2 3 do echo $i ; done
bash: syntax error near unexpected token `done'

Astuce: Utiliser la combinaison de touches ctrlX puis ctrlE

Interprétation des caractères

ANSI-C Quoting (Bash Reference Manual)

:

Placeholder

Initialisation de fichier

: > un_fichier
: >> info_connexion

if avec pass

$ if [[ -f nexistepas ]] ; then
:
else
    echo Aucun fichier
fi
Aucun fichier

Déclarer les variables

Intérêt

L’intérêt de déclarer les variables est notamment

Entiers

Déclaration

declare -i nombre

Faciliter les opérations arithmétiques

#!/bin/bash
set -euo pipefail
declare -i nombre
nombre=4
nombre=$nombre/2
echo "nombre = $nombre" # Affiche 2

# Non déclarée
autre=6
autre=$autre/2
echo "autre = $autre" # Affiche 6/2

Provoquer une erreur en cas d’affectation d’un autre type

#!/bin/bash
set -euo pipefail
declare -i nombre

# Tentative d'affectation d'une chaîne
nombre="quatre" # Erreur: unbound variable

Déclarer plusieurs variables simultanément

declare -i x y z

Utilisation :

$ x=2
$ y=x+9
$ echo $y
11

Constantes

Déclaration

Première méthode

readonly constante=1

Seconde méthode

declare -r constante=1

En cas de modification non souhaitée

constante=3

Cette instruction instruction provoque une erreur:

constante: readonly variable

Exportation

Déclaration d’une variable exportée:

declare -x variable_exportee

Exemple

Voici un programme correctement déclaré:

#!/bin/bash
set -euo pipefail

echo $variable_exportee
echo "Fin du programme"
$ ./var_export
./var_export: line 4: variable_exportee: unbound variable

Si on exporte la variable depuis le Shell:

$ export variable_exportee=45
$ ./var_export
45
Fin du programme

Suppression de la variable exportée

unset variable_exportee
$ ./var_export
./var_export: line 4: variable_exportee: unbound variable

Ajout de la déclaration:

#!/bin/bash
set -euo pipefail
declare -x variable_exportee

echo $variable_exportee
echo "Fin du programme"
$ ./var_export

Fin du programme

Tableaux

Déclaration

declare -a fichiers

Affectation

Créons un ensemble de fichiers:

$ touch {a..f}.txt

Affecter un tableau

La bonne manière de définir le tableau avec l’expansion:

$ fichiers=(*)
0 1 2 3 4 5
a.txt b.txt c.txt d.txt e.txt f.txt

Voir le paragraphe Mauvaise méthode d’affectation d’un tableau

Mauvaise méthode d’affectation
$ fichiers=*
$ echo $fichiers
a.txt b.txt c.txt d.txt e.txt f.txt

Bien que cela ait semblé fonctionner, ce qui suit montre que ce n’est pas le cas:

$ echo "$fichiers"
*
$ echo "${fichiers[0]}"
*
$ echo "${fichiers[1]}"

$ echo "${fichiers[2]}"

$ echo "${fichiers[@]}" # Tous les éléments du tableau
*

Accéder aux éléments

Accès par index

$ echo "${fichiers[0]}"
a.txt
$ echo "${fichiers[1]}"
b.txt

Utiliser tous les éléments du tableau

$ echo "${fichiers[@]}" # Tous les éléments du tableau
a.txt b.txt c.txt d.txt e.txt f.txt

Longueur du tableau

$ echo ${#fichiers[@]}
6

Découpage de tableau

On utilise le symbole : :

$ echo ${fichiers[@]:3:2}
d.txt e.txt

Parcours

IFS et les tableaux

Modifier cette variable permet de gérer correctement les noms de fichiers avec des espaces

La variable IFS doit de préférence être déclarée comme suit:

export IFS=$'\n\t'

Parcours avec boucle for

for i in ${!fichiers[@]}
do
    echo "fichiers[$i]: ${fichiers[$i]}"
done

Variante de boucle for

for ((i=0; i<${#fichiers[@]}; i++))
do
    echo "fichiers[$i]: ${fichiers[$i]}"
done

Les variables spéciales $* et $@

Utilité

$* et $@

Différence

#!/bin/bash

echo "Using \"\$*\":"
for a in "$*"; do
    echo $a;
done

echo -e "\nUsing \$*:"
for a in $*; do
    echo $a;
done

echo -e "\nUsing \"\$@\":"
for a in "$@"; do
    echo $a;
done

echo -e "\nUsing \$@:"
for a in $@; do
    echo $a;
done

Ressources:

Affichage

declare | grep nom_tableau

Fonctions

Déclaration

traiter_info(){
    declare reference="${1}"
    declare cible="${2}"
    declare option="${3}"

    if [[ -z "${reference}" ]] ; then
        echo "Reference est vide"
        echo "Envoi dans ${cible} sans référence avec ${option}"
    else
        echo "Envoi dans ${cible} depuis ${reference} avec ${option}"
    fi
}

Utilisation

traiter_info "data123" "cible principale" "attention"
traiter_info "" "cible principale" "attention"

Vérification de l’existence

declare -f nExistePas > /dev/null || ( echo "Fonction nExistePas non déclarée" >&2 ; exit 2 )
traiter_info ()
{
    declare reference="${1}";
    declare cible="${2}";
    declare option="${3}";
    if [[ -z "${reference}" ]]; then
        echo "Reference est vide";
        echo "Envoi dans ${cible} sans référence avec ${option}";
    else
        echo "Envoi dans ${cible} depuis ${reference} avec ${option}";
    fi
}

Portée de la variable

La portée d’une variable peut être réduite à la fonction dans laquelle elle est déclarée:

# Portée
portee_limitee(){
    declare variable_locale="Je suis limitée"
}
portee_non_limitee(){
    variable_locale_visible="Je suis accessible partout"
}
portee_limitee
portee_non_limitee
echo ${variable_locale_visible} # Affiche: Je suis accessible partout
echo ${variable_locale} # Erreur: variable_locale: unbound variable

Ceci est une pratique recommandée lors de l’utilisation de fonctions

Vérification avec declare -f

Il ne s’agit pas de déclaration ici mais d’une vérification

declare -f ma_fonction

Le code d’erreur renvoyé est