But : comprendre le fonctionnement de getopt en Bash pour éviter la multiplications de script là où un seul générique pourrait suffire.
Prérequis : savoir faire des scripts Bash, connaître la substitution de commande et savoir manipuler les arguments.
Difficulté : 2 (moyen)
Pour ceux qui codent en Perl, vous connaissez déjà sûrement le module GetOpt et plus particulièrement son extension GetOpt::Long (ou encore le module getopt du langage Python). Mais pour ceux qui souhaitent juste faire un script qui enchaine les grep et les cut pour pouvoir compter le nombre d'occurence d'une clé en fonction de certains critères, il peut être utile de pouvoir le faire uniquement en Bash plutôt que de se lancer dans un script Perl de plus. Dans cet article, je me propose donc de vous présenter la commande getopt illustrée d'un exemple simple.
Voici à quoi ressemble la première étape :
1 2 3 4 5 6 7 |
OPTS=$( getopt -o h -l home,nb_fichiers: -- "$@" ) if [ $? != 0 ] then exit 1 fi eval set -- "$OPTS" |
Ligne 1 :
- getopt est le nom de la commande
- -o pour les options courtes, c'est à dire une lettre par option
- h sera une option courte, pour afficher l'aide par exemple
- -l pour les options longues, c'est à dire un ensemble de lettres, comme un mot
- home sera une option longue simple
- nb_fichiers: sera une option longue attendant un argument en paramètre
- - - pour le comportement par défaut
Pour analyser les options, on utilise une boucle :
1 2 3 4 5 6 7 8 9 10 11 |
while true ; do case "$1" in -h) usage; exit 0;; --home) showHome; shift;; --nb_fichiers) echo "Afficher le nombre de fichiers dans le répertoire $2"; shift 2;; --) shift; break;; esac done |
Analysons cette portion de code :
- while true; do [..] done : une simple boucle while qui sera exécutée jusqu'à ce que le script soit terminé ;
- case $1 in [..] esac : on regarde le premier argument et on compare son résultat à l'un des cas attendus par le script ;
- -h : si $1 vaut -h, alors on exécute la fonction usage() et on termine le script ;
- - -home : si $1 vaut - -home, alors on exécute la fonction showHome() et on supprime l'argument $1 ;
- - -nb_fichiers : si $1 vaut - -nb_fichiers, alors on affiche la ligne avec le nom du répertoire donné dans $2 et on supprime les arguments $1 et $2 ;
- - - : si $1 vaut - - on supprime l'argument $1 et on effectue un break (comportement de fin attendu par case [..] esac)
Le script final, de mon cru et sous licence GPL ;), pourra ainsi ressembler à ceci :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
#!/bin/bash function usage(){ printf "Utilisation du script :\n" printf "\t--home : affiche le chemin vers le home de l'utilisateur courant ;\n" printf "\t--nb_fichiers repertoire : affiche le nombre de fichiers contenus dans le répertoire ;\n" printf "\t-h : affiche ce message.\n" } if [ $# -eq 0 ] then usage fi function showHome(){ echo "Home de l'utilisateur $USER"; echo $HOME; } function nbFichiers(){ if [ -d $1 ] then nb=$( ls $1 | wc -l ) echo "$1 contient $nb fichiers et répertoires." else echo "Ce n'est pas un répertoire !" usage exit 0 fi } OPTS=$( getopt -o h -l home,nb_fichiers: -- "$@" ) if [ $? != 0 ] then exit 1 fi eval set -- "$OPTS" while true ; do case "$1" in -h) usage; exit 0;; --home) showHome; shift;; --nb_fichiers) echo "Afficher le nombre de fichiers et répertoires dans le répertoire $2"; nbFichiers $2; shift 2;; --) shift; break;; esac done exit 0 |
Vous voilà parés pour faire de jolis scripts Shell/Bash complexes avec des arguments et des options dans tous les sens 😉 !
Un grand merci à Hautbit et Yoann M. pour leur relecture et nos discussions.
Un remerciement tout particulier à Sylvain P. pour m'avoir fait découvrir cette merveilleuse commande Bash !
KooK
octobre 14, 2012 à 12:08
Salut,
Une remarque sur le code bash, pas sur getopt qui est bien présenté (même si par nature, ça fait très formule magique comme fonction).
$ mkdir "repertoire avec espaces"
$ bash ./test_getopts.sh --nb repertoire\ avec\ espaces/
Ce n'est pas un répertoire !
...
Quand on n'a pas la maîtrise de ce qui entre comme dans le cas d'une action d'un utilisateur (!), il faut être très prudent et blinder le code.
Les variables sont à protéger avec "$1" ou même "${1}".
Bonne continuation.
KooK
Nolwenn
octobre 15, 2012 à 10:24
Merci pour l'astuce, il est vrai que je n'y ai pas pensé du tout, étant donné que j'ai pris l'habitude de ne jamais mettre d'espace dans un nom de répertoire/fichier. Ce qui n'est malheureusement pas toujours le cas de tout le monde, nous avons chacun nos petite manies. En plus, j'ai appris une nouvelle astuce de programmation 😉 !
Phinestra
février 4, 2013 à 5:18
bonjour,
j'aimerai savoir comment faire si je veux à la fois rentrer 3 options. par exemple pour une connexion à un switch j'ai besoin de 3 options longues.
merci pour toute aide.
phi.
Nolwenn
février 5, 2013 à 10:36
Bonjour,
il vous suffit de saisir dans le switch autant d'options que nécessaires puis de gérer le traitement des options renseignées.
Vous n'aurez alors qu'à saisir vos trois options longues les unes à la suite des autres comme dans n'importe quel programme. getopt est vraiment simple à utiliser une fois que l'on a compris le fonctionnement de base 🙂 !
Cordialement,
Nolwenn
KooK
septembre 5, 2013 à 2:27
En essayant d'intégrer des options proprement dans un script, j'ai lu que finalement getopt n'est pas forcément une bonne recommandation parce que non standard et implémenté de façons différentes.
Ici : http://mywiki.wooledge.org/BashFAQ/035
on trouve différentes propositions pour analyser les options.
Il me semble que du coup le code de cet article fonctionnerait tout aussi bien sans getopt, avec juste la boucle.
Nolwenn
septembre 5, 2013 à 4:20
Bonjour KooK,
merci beaucoup pour cette remontée d'informations. Je vous avoue que je suis surprise étant donné que j'avais lu l'inverse l'année dernière, quelques semaines avant de rédiger cet article. Les normes auraient-elles changé aussi vite ?
Si vous avez l'occasion de tester la boucle sans getopt(s), je serais ravie d'en connaître le verdict 🙂 !
Cordialement, Nolwenn
Max
septembre 6, 2013 à 4:53
Si mes souvenirs sont bons, j'avais géré mes arguments dans un script Bash avec une boucle identique, mais sans getop.
Donc je pense que ça marche.
Leaurendmalade
décembre 2, 2017 à 12:30
Merci pour ce tuto :). C'est exactement ce dont j'avais besoin: un tuto avec un exemple simple pour comprendre rapidement comment faire un script bash avec des options. C'est quand même plus agréable que de se perdre dans les limbes d'internet sur des modes d'emploi abscons de 42 pages…
En revanche, pour que ce script fonctionne chez moi (Mac OSX 10.6.8), j'ai dû:
— commenter «eval set -- "$OPTS"»
— remplacer le «true» de la boucle «while» par un booléen valant «true» si «$# != 0» et «false» si aucun argument «$# == 0», car sinon, après l'appel de «usage», l'exécution rentrait dans la boucle «while» qui itérait à l'infini (et c'est long l'infini, surtout vers la fin!)