Comment gérer les commutateurs dans un script shell?

17

Existe-t-il des outils intégrés qui reconnaîtront -xet en --xxxxtant que commutateurs, et non des arguments, ou devez-vous parcourir toutes les variables d'entrée, tester les tirets, puis analyser les arguments par la suite?

user394
la source

Réponses:

16

Utilisez getopts.

Il est assez portable comme dans la spécification POSIX. Malheureusement, il ne prend pas en charge les options longues.

Voir aussi ce getopts tutoriel gracieuseté du wiki bash-hackers et cette question de stackoverflow.

Si vous n'avez besoin que d'options courtes, le modèle d'utilisation typique pour getopts(à l'aide d'un rapport d'erreurs non silencieux) est:

# process arguments "$1", "$2", ... (i.e. "$@")
while getopts "ab:" opt; do
    case $opt in
    a) aflag=true ;; # Handle -a
    b) barg=$OPTARG ;; # Handle -b argument
    \?) ;; # Handle error: unknown option or missing required argument.
    esac
done
jw013
la source
3
Il convient de mentionner que cela getoptdoit toujours être vérifié en tant que GNU getopt avant de l'utiliser, mais vous ne devez pas l'utiliser de toute façon car il getoptsest plus portable (et généralement plus agréable) de toute façon. Si vous avez besoin d'appeler pour une raison quelconque, l' appeler d'une manière spécifique à GNU, et veiller à ce que GETOPT_COMPATIBLEn'est pas dans l'environnement.
Chris Down
Que fait le côlon while getopts "ab:" opt?
user394
1
@ user394 L' :après une lettre d'option signifie qu'elle nécessite un argument. A :comme premier caractère signifie supprimer les messages d'erreur.
jw013
22

Je suppose que vous utilisez bash ou similaire. Un exemple:

all=false
long=false

while getopts ":hal" option; do
  case $option in
    h) echo "usage: $0 [-h] [-a] [-l] file ..."; exit ;;
    a) all=true ;;
    l) long=true ;;
    ?) echo "error: option -$OPTARG is not implemented"; exit ;;
  esac
done

# remove the options from the positional parameters
shift $(( OPTIND - 1 ))

ls_opts=()
$all && ls_opts+=( -a )
$long && ls_opts+=( -l )

# now, do it
ls "${ls_opts[@]}" "$@"
glenn jackman
la source
1
+1 pour une utilisation +=avec un tableau. Je ne savais pas que tu pouvais faire ça. Agréable!
James Sneeringer
5

Vous devez écrire un cycle pour analyser les paramètres. En effet, vous pouvez utiliser la getoptscommande pour le faire facilement.

Ceci est un exemple simple de getoptsla page de manuel:

aflag=
bflag=
while getopts ab: name
do
    case $name in
    a)    aflag=1;;
    b)    bflag=1
          bval="$OPTARG";;
    ?)    printf "Usage: %s: [-a] [-b value] args\n" $0
          exit 2;;
    esac
done
if [ ! -z "$aflag" ]; then
    printf "Option -a specified\n"
fi
if [ ! -z "$bflag" ]; then
    printf 'Option -b "%s" specified\n' "$bval"
fi
shift $(($OPTIND - 1))
printf "Remaining arguments are: %s\n" "$*"
andcoz
la source
2

J'ai récemment écrit un script pour un travail qui était polyvalent et permettait plusieurs types de commutateurs dans n'importe quel ordre. Je ne peux pas divulguer le script complet pour des raisons juridiques évidentes (sans parler du fait que je ne l'ai pas avec moi pour le moment), mais en voici la chair .. vous pouvez le mettre dans un sous-programme et l'appeler à la fin de votre script:

options () {

    if [ -n "$1" ]; then # test if any arguments passed - $1 will always exist
        while (( "$#" )); do  # process ALL arguments
            if [ "$1" = ^-t$ ]; then # -t short for "test"
                # do something here THEN shift the argument
                # shift removes it from $@ and reduces $# by
                # one if you supply no argument
                shift

            # we can also process multiple arguments at once
            elif [[ "$1" =~ ^--test=[:alnum:]{1,8}$ ]] && [[ "$2" =~ ^-t2$ ]] && [[ -n "$3" ]]; then # check for 3 arguments
                # do something more then remove them ALL from the arg list    
                shift 3
            else
                echo "No matching arguments!"
                echo "Usage: [script options list here]"
            fi
        done
    else
        echo "Usage: [script options list here]"
        exit 0
    fi
}

options "$@" # run options and loop through/process ALL arguments

Je recommande de limiter votre script bash à moins de 400 lignes / 15k caractères; mon script susmentionné a dépassé cette taille et est devenu très difficile à travailler. J'ai commencé à le réécrire en Perl, ce qui est beaucoup mieux adapté à la tâche. Gardez cela à l'esprit lorsque vous travaillez sur vos scripts dans bash. Bash est idéal pour les petits scripts et les oneliners, mais quelque chose de plus complexe et vous feriez mieux de l'écrire en Perl.

Remarque, je n'ai pas testé ce qui précède, donc cela ne fonctionne probablement pas, mais vous en avez une idée générale.

abat-jour
la source
La façon dont vous appelez optionsà la fin n'est pas correcte, elle reviendra -bash: syntax error near unexpected token $@. Appelez-le comme options "$@".
Chris Down
Ouais, j'ai mélangé Perl et Bash. Corrigée.
laebshade
Cette whilecondition ne devrait pas être à la (($#))place?
manatwork
Pourquoi auriez-vous besoin de deux jeux de parenthèses $#? Edit: vous avez raison. Fixé àwhile (( "$#" ))
laebshade