User Tools

Site Tools


informatique:linux:programmation_shell

This is an old revision of the document!


programmation langage bash shell

Programmation Shell

  • . <CONFIG_FILE> ⇒ permet de faire un include (notez bien le point + espace “. ”)
  • !! ⇒ est remplacé par la dernière commande (différent de $_ qui contient uniquement le dernier paramètre de la dernière commande)
$ touch toto
$ echo !!
echo touch toto
touch toto

Les variables

En bash, chaque variable est précédée de $. On affecte un contenu ainsi : VAR=“toto” et on récupère le contenu en ajoutant $ devant.

NOM="robert"
echo "Salut, je suis le gros $NOM"

On peut les manipuler sans les déclarer proprement au préalable ; mais on peut être clean et les définir (declare VAR) ou les supprimer (unset VAR).

Tests de définition

Pour tester si une variable est non définie : ${VAR?message d'erreur} ou vide : ${VAR:?message d'erreur}.

unset VAR
${VAR?var non def}     # -bash: VAR: variable non def (sur stderr)
declare VAR
${VAR?var non def}     # -bash: VAR: variable non def (sur stderr)
VAR=
${VAR?var non def}     # (rien car VAR est définie)
${VAR:?vide}           # -bash: VAR: vide (sur stderr)

Pour utiliser une valeur par défaut au cas ou la variable n'est pas définie : ${VAR-non-def} ; on peut également vérifier si cette dernière est non nulle (non-vide) : ${VAR:-non-def}.

declare VAR
echo ${VAR-defaut}     # (vide car VAR est définie)
echo ${VAR:-defaut}    # vide
 
unset VAR
echo ${VAR-defaut}     # defaut
echo ${VAR:-defaut}    # defaut

Cela ne définit ni n'affecte VAR ; pour cela il faut utiliser la syntaxe ${VAR=defaut} et ${VAR:=defaut}.

declare VAR
echo ${VAR=defaut}     # (ne produit rien)
echo ${VAR:=defaut}    # defaut (VAR prend également cette valeur)

Manipulation des variables

  • ${#VAR} renvoie la longueur de la variable VAR. Si VAR est un tableau : ${#VAR[@]}
  • ${VAR} permet de délimiter le nom de variable ; c'est utile dans le cas suivant par exemple :
scp user@server:/tmp/fic.txt ${REP_LOCAL}_/

Sans les {}, l'interpréteur présumera que la variable se nomme $REP_LOCAL_ et non $REP_LOCAL.

  • on peut tronquer une variable : ${VAR:POS} ⇒ renvoie le contenu à partir de la position POS (comptabilisée à partir de 0). Si POS est négative (il faut la mettre entre parenthèses), on compte à partir de la fin de la chaine.
  • ${VAR:POS:LONG} : renvoie la sous-chaine de longueur LONG à partir de la position POS5e lettre
VAR=undeuxtroisquatre
echo ${VAR:2}          # deuxtroisquatre
echo ${VAR:(-6)}       # quatre
echo ${VAR:(-6):2}     # qu

On peut utiliser des motifs (qui en ont l'aspect mais ne sont pas exactement des expressions régulières) pour substituer des bouts de chaine :

  • ${VAR#PAT} : supprime la plus courte chaine répondant à la pattern PAT ; on peut partir de la fin de la chaine avec % à la place de #
  • ${VAR##PAT} : supprime la plus longue chaine répondant à la pattern PAT (idem : %%)
  • ${VAR/PAT/SUBST} pour remplacer par SUBST la première occurrence du contenu de VAR répondant à la pattern PAT ; respectivement // pour toutes les occurrences. On peut partir du début # (par défaut) ou de la fin de la chaine %, mais c'est dans tous les cas le motif le plus grand qui sera remplacer (pas de ## ni %%).
VAR=undeuxtroisquatre
echo ${VAR#u*e}        # uxtroisquatre
echo ${VAR##u*e}       # (rien !)
echo ${VAR%u*e}        # undeuxtroisq
echo ${VAR/trois/3}    # undeux3quatre
echo ${VAR//e/E}       # undEuxtroisquatrE

source : https://www.patpro.net/blog/index.php/2006/04/07/20-manipulations-sur-les-variables-dans-bash/

Variables spécifiques

Ce sont des variables liées au contexte du script.

  • $0 est le nom du script
  • $1 $2 … $10 les arguments du script. On peut les décaler grâce à la commande shift ($2 devient $1, $3 devient $2, etc)
  • $_ renvoie le dernier argument de la dernière commande
  • $* liste de tous les arguments
  • $@ liste de tous les arguments (équivalent à $*)
  • $# renvoie le nombre d'argument du script
  • $$ renvoie le PID du script courant
  • $! renvoie le PID de la dernière commande
  • $? renvoie le retour (code d'erreur par exemple) de la dernière commande
  • IFS=“\n” (Internal Field Separator) le séparateur de champ est “ENTER” (utilisée par la commande read)

Les tableaux

Les enregistrements commencent à l'index 0. Ainsi pour récupérer le contenu de la 5ème case on doit taper dans l'index 4.

  • TAB=( un deux trois ) : définition d'un tableau de 3 cases
  • TAB[0]=val : affectation du premier enregistrement du tableau TAB
  • ${TAB[0]} : contenu du premier enregistrement du tableau TAB
  • $TAB : équivalent de ${TAB[0]}
  • ${TAB[*]} : désigne l'ensemble des enregistrements du tableau TAB

Comme pour les variables classiques, on peut récupérer la longueur en le précèdent de # :

  • longueur de la 3e case : ${#TAB[2]}
  • taille du tableau (nombre de case de celui-ci) : ${#TAB[*]} (ou ${#TAB[@]})

Les fonctions

read

read toto permet de demander une saisie clavier à l'utilisateur.

Pour afficher un texte avant la saisie on utilise -p ; et on peut récupérer plusieurs saisie d'un seul coup :

read -p "Quel est le nombre indique sur votre CB ? Et le cryptogramme visuel de derriere ?" CARD_NUMBER CRYPTO

Expressions arithmétiques

Pour faire un calcul il faut l'encadrer de $((calcul)) ou $[calcul]

echo 1+1=$((1+1))

La fonction puissance s'écrit ** :

echo 2^5=$((2**5))

Pour incrémenter une variable : plusieurs possibilités :

  • z=`expr $z + 1`
  • z=$(($z+1))
  • z=$((z+1)) (entre doubles parenthèses, le $ est optionnel)
  • (( z += 1 ))

Instructions conditionnelles

if

test expr

ou

if [ expr ]
  then
  else
fi
  • Exemple sur une seule ligne (-n renvoie TRUE si la chaine n'est pas vide) :
if [ -n "" ]; then echo "1"; else echo "0"; fi
  0

Conditions multiples

Pour tester des conditions multiples, on doit encadrer les tests avec un double crochet :

if [[ TRUE && TRUE || FALSE ]]
then
  echo "TRUE"
else
  echo "TRUE QUAND MÊME !"
fi

On peut également procéder ainsi :

if [ TRUE ] && [ TRUE ] || [ FALSE ]

Les conditions multiples sans parenthèses sont lues dans l'ordre d'apparition (de gauche à droite), ici cela équivaut à : (TRUE ET TRUE) OU FALSE. Donc TRUE quand même.

  • Structure avec “elif” (“elseif” dans d'autres langages) qui permet de faire des tests à la suite :
if [ expr ]
  then
elif
  then
elif
  then
[..]
else
fi

Expression sur les fichiers

  • -d file : existe et est un répertoire
  • -e file : existe
  • -f file : existe et non répertoire
  • -h file : si le fichier est un lien symbolique
  • -s file : existe et est de taille non nulle
  • -r file : existe et droit en lecture
  • -w file : existe et droit en écriture
  • -x file : existe et droit d'exécution
  • -O file : si le fichier nous appartient
  • -G file : si le fichier appartient à notre groupe

Chaînes de caractères

  • str1 = str2
  • str1 != str2
  • -z str : vrai si chaîne de longueur nulle
  • -n str : vrai si chaîne de longueur non nulle

Nombres, comparaison

  • n1 -eq n2 égaux
  • n1 -ne n2 non égaux
  • n1 -gt n2 plus grand que
  • n1 -ge n2 plus grand ou égal
  • n1 -lt n2 plus petit que
  • n1 -le n2 plus petit ou égal

Opérateurs

  • ! unaire de négation
  • -a et logique
  • -o ou logique
  • ( expr ) paranthésage
  • \( et \) sinon interprétées par le shell

case

Test plusieurs valeurs de <chaîne> (~ifs imbriqués).

case <chaîne> in
 motif1)
    echo "motif 1"
    echo "C'est le premier !!"
    ;;
 motif2|motif3)
    echo "motif 2 ou motif 3"
    ;;
 motif*)
    echo "tous les autres motifs"
    ;;
 *)
    echo "Tout le reste"
    ;;
esac

for

Permet de créer une boucle déterministe : pour chaque valeur de i, on exécute la boucle.

Exemples :

for (( i = 1; i <= 5; i++ ))
  do
  echo -n "$i "
done

Affichera : 1 2 3 4 5 .

Cela équivaut à : for i in {1..5} qui est une autre façon d'incrémenter i de 1 à 5.

On peut aussi lui fournir une liste de mots :

for i in serveur1 serveur2 serveur3
do
   ssh $i "(uname -r)"
done

On peut l'exécuter sur une seule ligne de commande en respectant cette syntaxe :

for i in serveur1 serveur2 serveur3; do ssh $i "(uname -r)"; done

break

Pour sortir prématurément d'une boucle for, while ou repeat, on utilise le mot-clé break :

for i in {1..5}
do
   if [ $i -eq 3 ]
   then
      echo "Huston nous avons un probleme !"
      break
   fi
   echo $i
done

Ce qui donne :

1
2
Huston nous avons un probleme !

continue

Pour sortir de l'occurrence courante de la boucle, on utilise continue. On quitte l'occurrence courante et on passe à la suivante :

for i in {1..5}
do
   if [ $i -eq 3 ]
   then
      echo "Huston nous avons un probleme !"
      continue
   fi
   echo $i
done

Ce qui donne :

1
2
Huston nous avons un probleme !
4
5

while

Exécute la boucle tant que la condition suivant l'instruction while est remplie :

exemple : lire un fichier ligne par ligne

while read line
do
  echo $line
done < fic.txt

ou sur une seule ligne :

while read n; do echo $line; done < fic.txt

until

Exécute la boucle jusqu'à ce que la condition soit remplie :

until <condition>
do
   echo "Toujours pas !"
done

select

Permet de créer un menu interactif, c'est à dire une saisie utilisateur ;

  • $PS3 permet de définir le prompt
  • $REPLY contient l'index de la réponse
PS3="Prompt du menu"
select i in {2..5}
do
    # $REPLY contient l'index de la réponse ;
    # $i contient la valeur correspondante
    echo $REPLY-$i
    
    # pour sortir du menu (car il s'agit d'une boucle)
    break
done

Divers

en vrac

  • commande rsh
rsh -4 -n $serveur $commande >/dev/null 2>&1
  • commande ping limitée à 3 envois avec un timeout de 0.05s
ping -c3 -w 0.05 soekris-${cpt} >/dev/null 2>&1
retour=$?
  • attente d'un SIGUSR1 (30) pour terminer le script :
trap "echo 'Exiting..' ; exit 0" 30

Substitution de chaine

Pour n'afficher qu'une partie d'une chaine (une sous-chaine donc), on utilise la syntaxe ${tst:<offset>:<length>}. L'offset, c'est l'origine (elle commence à 0 pour le premier caractère, comme souvent en informatique), et length c'est le longueur de la chaine affichée (facultative si on veut afficher jusqu'à la fin).

Par exemple :

TEST="Je suis chanceux"
 
# on affiche la sous-chaine à partir du caractère n°3 (soit le 4ème caractère) :
echo ${TEST:3}
suis chanceux
 
# idem, mais on se limite à 4 caractères affichés :
echo ${TEST:3:4}
suis
 
# on peut spécifier un offset négatif pour compter l'offset à partir de la fin de la chaîne
# cependant il faudra ajouter un espace avant le "-" (ou l'encadrer de parenthèses)
echo ${TEST: -8}
chanceux
echo ${TEST:(-8)}
chanceux

Remplacement de chaine

Syntaxe : ${<parameter>/<pattern>/<string>}

<parameter> c'est la chaine d'origine, <pattern> l'expression de la chaine recherchée, et <string> la chaine de remplacement.

<pattern> peut commencer par ces caractères spéciaux (à défaut, il ne remplace que la première occurrence rencontrée) :

  • / pour rechercher toute les occurrences
  • # pour rechercher une chaîne qui commence par
  • % pour rechercher une chaîne qui se termine par
TEST="toto titi tata"
 
# remplacer tous les "a" par des "i"
echo ${TEST//a/i}
toto titi titi
 
# ne remplacer que "a" de fin de chaîne
echo ${TEST/%a/i}
toto titi tati
 
# ne remplacer que le premier "t", par un "p"
echo ${TEST/#t/p}
poto titi tata

Modifier la casse

Pour modifier la casse, on utilise la syntaxe :

  • ${parameter^pattern} pour passer de minuscules à majuscules
  • ou ${parameter,pattern} pour passer de majuscules à minuscules

<pattern> étant optionnel, auquel cas seule la première lettre sera prise en compte :

TEST="toto TITI tata"
 
echo ${TEST^}
Toto TITI tata

On peut doubler les lettres pour traiter toute la chaine de caractère :

echo ${TEST^^}
TOTO TITI TATA
 
echo ${TEST,,}
toto titi tata

Enfin, on peut spécifier la pattern à la suite :

# on UPPERCASE tous les "t"
echo ${TEST^^t}
ToTo TITI TaTa
 
# on UPPERCASE les "t" et les "a"
echo ${TEST^^[ta]}
ToTo TITI TATA

Préfixes et suffixes

Syntaxe : ${<parameter>#<word>} pour les préfixes, ${<parameter>%<word>} pour les suffixes, ce qui va supprimer la plus courte occurrence de la chaîne. Pour supprimer la plus longue occurrence, on utilisera respectivement ## et %%.

On s'en sert souvent pour manipuler les chemins et les noms de fichier :

FIC=/chemin/ve.rs/mon/fichier.ext
 
# afficher uniquement son extension
# (on supprime le plus long préfixe se terminant par ".")
echo ${FIC##*.}
fichier.ext
 
# afficher le chemin du fichier :
# (on supprime le suffixe le plus court contenant un "/")
echo ${FIC%/*}
/chemin/ve.rs/mon

Liens

informatique/linux/programmation_shell.1442396496.txt.gz · Last modified: 2015/09/16 09:41 by pteu