Accessibility Tools

- Le blog participatif de bioinformatique francophone depuis 2012 -

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.

Script bash en cours d'édition sous VIM
Script bash en cours d'édition sous VIM (auteur : Nolwenn) | Licence Art Libre

Voici à quoi ressemble la première étape :

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
  • -l pour les options longues, c'est à dire un ensemble de lettres, comme un mot
  • - - pour le comportement par défaut

Pour analyser les options, on utilise une boucle :

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 :

#!/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 !




Commentaires

8 réponses à “Astuce : ajouter des options dans un script Bash avec getopt”

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

    1. 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 😉 !

  2. Avatar de Phinestra
    Phinestra

    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.

    1. Avatar de Nolwenn

      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

  3. 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/​B​a​s​h​F​A​Q​/​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.

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

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

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

Laisser un commentaire

Pour insérer du code dans vos commentaires, utilisez les balises <code> et <\code>.