Difference between revisions of "HelloBash Ep3"

From Tmplab
(Les branches conditionnelles)
Line 100: Line 100:
 
On utilise des structures logiques pour changer l'exécution du code en fonction du contexte.
 
On utilise des structures logiques pour changer l'exécution du code en fonction du contexte.
  
'''IF'''
+
'''IF / ELIF / FI'''
  
 
  file=/etc
 
  file=/etc
  if [ -d "$file" ] ; then
+
  if [[ -d "$file" ]] ; then
 
   echo "the file exists and is a directory"
 
   echo "the file exists and is a directory"
  elif [ -e "$file" ] ; then
+
  elif [[ -e "$file" ]] ; then
 
   echo "the file exists"
 
   echo "the file exists"
 
  else
 
  else
 
   echo "no such file"  
 
   echo "no such file"  
 
  fi
 
  fi
 +
 +
La directive conditionnelle suit le premier embranchement dont le résultat est valide, ici en utilisant un test (voir suite) mais if supporte l'exécution en direct
 +
 +
if COMMANDE; then ... ; elif COMMANDE; then ...; else ...; fi
 +
 +
if grep "#" /etc/adduser.conf &>/dev/null; then echo ok; fi
 +
 +
 +
""test""
 +
 +
Arrêtons nous sur les tests qui ont été fait dans l'exemple précédent. Les 3 formulations suivantes sont équivalentes
 +
 +
[ -d /etc ]
 +
[ <escape> char char char \ ] -d /etc ]]
 +
test -d /etc
 +
 +
Il est conseillé de nos jours d'utiliser <code>[[</code> plutôt que le simple crochet car le premier accepte plus d'options, [http://mywiki.wooledge.org/BashFAQ/031 voir ici pour une explication étendue].
 +
 +
 +
 +
 +
 +
== Les boucles==
 +
 +
Elles sont utiles pour itérer sur une liste composée d'espaces, de tabulation ou de sauts de ligne servant de séparateur
 +
 +
PROTIP: pourquoi modifier $IFS ?
  
 
'''FOR'''
 
'''FOR'''
Line 160: Line 187:
 
  myscript.sh -h -a yo
 
  myscript.sh -h -a yo
  
== Les boucles==
 
 
== La structure logique==  
 
== La structure logique==  
 +
 +
Votre s
 +
 +
 
== Le contrôle des statuts de sortie==
 
== Le contrôle des statuts de sortie==
 
== Faire des tests conditionnels ==
 
En cours de rédaction...
 

Revision as of 14:57, 26 November 2019

Retour à la liste des épisodes

Ecrire et exécuter des scripts

Un premier script

echo "#!/bin/bash" > /tmp/myscript.sh
echo "echo helloBash" >> /tmp/myscript.sh
cat /tmp/myscript.sh | bash

L'entête #! ("Shebang" ou "Crunchbang") indique quel est l'interpréteur à utiliser (ici Bash, mais on pourrait en utiliser d'autre)

L'extension .sh est une convention pour les scripts shell en général.

On voit qu'on peut utiliser le contenu brut du script comme STDIN de bash pour l'exécuter.

Exécuter un script

Pour l'exécuter il faut ajouter le droit d'exécution sur le fichier

chmod 755 /tmp/myscript.sh
bash -c /tmp/myscript.sh
/tmp/myscript.sh

Qualités et concepts inhérents aux scripts

Toutes les commandes disponibles interactivement sont ajoutables dans un script.

Un script doit :

  • être réutilisable - automatiquement (cron) ou avec des entrées utilisateurs différentes
  • ne faire que le nécessaire - usage des structures logiques et des tests
  • être programmé de manière logique et compréhensible - une structure claire et lisible
  • s'exécuter sans erreurs - détection des échecs et abandon éventuel


Les concepts suivants sont à maîtriser pour rédiger de bons scripts :

  • Les entrées utilisateurs
  • Les branches conditionnelles
  • Les boucles
  • La structure logique
  • Le contrôle des statuts de sortie

Gestion des arguments et des options

Avant d'aborder les concepts essentiels, petit détour par la gestion des éléments envoyés au script depuis la ligne de commande. Un exemple.

cat ./myscript.sh 
#!/bin/bash
echo "My name is $0"
echo "My first argument is $1"
 ./myscript.sh foo

On voit donc que la commande est stockée dans la variable $0 et le premier argument dans $1.

Bash ne sait pas nativement reconnaître des options, le script ne voit que des arguments les uns derrière les autres, accessibles dans des variables numérotées.

Une variable spéciale $@ contient tous les arguments passés à la commande, donc de $1 à $9.


cat ./myscript.sh 
#!/bin/bash
echo  "Arg1: $1 - Arg2: $2 - Arg3: $3 - Arg4: $4 - Arg5: $5 "
echo "All:  $@ "
./myscript.sh Epstein did not kill himself 

C'est la première manière de fournir des entrées utilisateurs : passer des arguments à la ligne de commande pour moduler le fonctionnement du script.

cat ./nocomment.sh 
#!/bin/bash
grep -Ev '^\s*([;#].*)?$' $@
# ./nocomment.sh /etc/adduser.conf

Notez que pour avoir des options i.e. des --this ou -a et donc avoir une ligne de commande plus riche et plus adaptable, on utilise getopts (voir plus loin).

Les entrées utilisateurs

On utilise des variables pour stocker les valeurs saisies par l'utilisateur.

#!/bin/bash
user_input=$1
echo "You typed: $user_input"
myscript.sh hello world

On peut également utiliser stdin comme moyen de passer des variables

#!/bin/bash 
std_in=$( cat - )  
echo ${std_in}

echo "hello bash" | myscript.sh

Les branches conditionnelles

On utilise des structures logiques pour changer l'exécution du code en fonction du contexte.

IF / ELIF / FI

file=/etc
if -d "$file"  ; then
  echo "the file exists and is a directory"
elif -e "$file"  ; then
  echo "the file exists"
else
  echo "no such file" 
fi

La directive conditionnelle suit le premier embranchement dont le résultat est valide, ici en utilisant un test (voir suite) mais if supporte l'exécution en direct

if COMMANDE; then ... ; elif COMMANDE; then ...; else ...; fi
if grep "#" /etc/adduser.conf &>/dev/null; then echo ok; fi


""test""

Arrêtons nous sur les tests qui ont été fait dans l'exemple précédent. Les 3 formulations suivantes sont équivalentes

[ -d /etc ]
[ <escape> char char char \ ] -d /etc ]]
test -d /etc

Il est conseillé de nos jours d'utiliser [[ plutôt que le simple crochet car le premier accepte plus d'options, voir ici pour une explication étendue.



Les boucles

Elles sont utiles pour itérer sur une liste composée d'espaces, de tabulation ou de sauts de ligne servant de séparateur

PROTIP: pourquoi modifier $IFS ?

FOR

for f in /etc/*; do
  -d "$f"  && echo "$f is a directory"
done

WHILE

sudo ls -d /etc/* |  while read f; do
  -d "$f"  && echo "$f is a directory"
done 

CASE

Case est un peu plus compliqué

read -p "say something (hello|bye|...) : " input
case $input in
  hello)
    echo "You said hello"
  ;;
  bye)
    echo "You said bye"
  ;;
  *)
    echo "You said something else..."
  ;;
esac

Combiner les structures

#! /bin/bash
while getopts "ha:" option ; do
 case "$option" in
   h) 
     echo "this is help"
   ;;
   a)
     echo "this is the -a option and its parameter '$OPTARG'"
   ;;
   *) 
     echo "unknown option"
   ;;
 esac
done
myscript.sh -h -a yo

La structure logique

Votre s


Le contrôle des statuts de sortie