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).

VAR peut être un tableau avec la syntaxe : VAR=( un deux trois ) ; les cases sont accessibles avec ${VAR[case]}

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 ''). <code bash> 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 </code> Pour tester si une variable est non définie : ''${VAR?message d'erreur}'' ou vide : ''${VAR:?message d'erreur}''. <code bash> 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) </code> 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}''. <code bash> 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 </code> Cela ne définit ni n'affecte VAR ; pour cela il faut utiliser la syntaxe ''${VAR=defaut}'' et ''${VAR:=defaut}''. <code bash> declare VAR echo ${VAR=defaut} # (ne produit rien) echo ${VAR:=defaut} # defaut (VAR prend également cette valeur) </code> 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[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[*]}'' =====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 : <code> read -p "Quel est le nombre indique sur votre CB ? Et le cryptogramme visuel de derriere ?" CARD_NUMBER CRYPTO </code> ===== Expressions arithmétiques ===== Pour faire un calcul il faut l'encadrer de ''<nowiki>$((calcul))</nowiki>'' ou ''$[calcul]'' echo 1+1=$((1+1)) La fonction puissance s'écrit ''<nowiki>**</nowiki>'' : echo 2^5=$((2**5)) Pour incrémenter une variable : plusieurs possibilités : * ''<nowiki>z=`expr $z + 1`</nowiki>'' * ''<nowiki>z=$(($z+1))</nowiki>'' * ''<nowiki>z=$((z+1))</nowiki>'' (entre doubles parenthèses, le $ est optionnel) * ''<nowiki>(( z += 1 ))</nowiki>'' ===== Instructions conditionnelles ===== ====if==== <code> test expr </code> ou <code> if [ expr ] then else fi </code> * Exemple sur une seule ligne (//-n// renvoie TRUE si la chaine n'est pas vide) : <code> if [ -n "" ]; then echo "1"; else echo "0"; fi 0 </code> ===Conditions multiples=== Pour tester des conditions multiples, on doit encadrer les tests avec un double crochet : <code> if [[ TRUE && TRUE || FALSE ]] then echo "TRUE" else echo "TRUE QUAND MÊME !" fi </code> On peut également procéder ainsi : <code> if [ TRUE ] && [ TRUE ] || [ FALSE ] </code> 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 : <code> if [ expr ] then elif then elif then [..] else fi </code> === 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 : <code> for (( i = 1; i <= 5; i++ )) do echo -n "$i " done </code> 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 : <code> for i in serveur1 serveur2 serveur3 do ssh $i "(uname -r)" done </code> On peut l'exécuter sur une seule ligne de commande en respectant cette syntaxe : <code> for i in serveur1 serveur2 serveur3; do ssh $i "(uname -r)"; done </code> ===break=== Pour sortir prématurément d'une boucle for, **while** ou **repeat**, on utilise le mot-clé **break** : <code> for i in {1..5} do if [ $i -eq 3 ] then echo "Huston nous avons un probleme !" break fi echo $i done </code> Ce qui donne : <code> 1 2 Huston nous avons un probleme ! </code> ===continue=== Pour sortir de l'occurrence courante de la boucle, on utilise **continue**. On quitte l'occurrence courante et on passe à la suivante : <code> for i in {1..5} do if [ $i -eq 3 ] then echo "Huston nous avons un probleme !" continue fi echo $i done </code> Ce qui donne : <code> 1 2 Huston nous avons un probleme ! 4 5 </code> ====while==== Exécute la boucle tant que la condition suivant l'instruction **while** est remplie : exemple : lire un fichier ligne par ligne <code> while read line do echo $line done < fic.txt </code> ou sur une seule ligne : <code>while read n; do echo $line; done < fic.txt</code> ====until==== Exécute la boucle jusqu'à ce que la condition soit remplie : <code> until <condition> do echo "Toujours pas !" done </code> ====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 <code> 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 </code> =====Divers===== ====en vrac==== * commande rsh <code bash> rsh -4 -n $serveur $commande >/dev/null 2>&1 </code> * commande ping limitée à 3 envois avec un timeout de 0.05s <code bash> ping -c3 -w 0.05 soekris-${cpt} >/dev/null 2>&1 retour=$? </code> * attente d'un ''SIGUSR1'' (30) pour terminer le script : <code bash> trap "echo 'Exiting..' ; exit 0" 30 </code> ==== 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 : <code bash> 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 </code> ==== 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// <code bash> 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 </code> ====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 : <code bash> TEST="toto TITI tata" echo ${TEST^} Toto TITI tata </code> On peut doubler les lettres pour traiter toute la chaine de caractère : <code bash> echo ${TEST^^} TOTO TITI TATA echo ${TEST,,} toto titi tata </code> Enfin, on peut spécifier la pattern à la suite : <code bash> # on UPPERCASE tous les "t" echo ${TEST^^t} ToTo TITI TaTa # on UPPERCASE les "t" et les "a" echo ${TEST^^[ta]} ToTo TITI TATA </code> ====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.1442396093.txt.gz · Last modified: 2015/09/16 09:34 by pteu