web-dev-qa-db-fra.com

Façon correcte de vérifier un indicateur de ligne de commande dans bash

Au milieu d'un script, je veux vérifier si un drapeau donné a été passé sur la ligne de commande. Ce qui suit fait ce que je veux mais semble laid:

if echo $* | grep -e "--flag" -q
then
  echo ">>>> Running with flag"
else
  echo ">>>> Running without flag"
fi

Y a-t-il une meilleure façon?

Remarque: je explicitement ne pas veux lister tous les drapeaux dans un commutateur/getopt. (Dans ce cas, de telles choses deviendraient la moitié ou plus du script complet. De plus, les corps du si juste définir un ensemble de vars)

43
BCS

Une alternative à ce que vous faites:

if [[ $* == *--flag* ]]

Voir aussi BashFAQ/035 .

Remarque : Cela correspondra également à --flags-off car c'est une simple vérification de sous-chaîne.

Je vois généralement cela avec une déclaration de cas. Voici un extrait du script git-repack :

while test $# != 0
do
    case "$1" in
    -n) no_update_info=t ;;
    -a) all_into_one=t ;;
    -A) all_into_one=t
        unpack_unreachable=--unpack-unreachable ;;
    -d) remove_redundant=t ;;
    -q) GIT_QUIET=t ;;
    -f) no_reuse=--no-reuse-object ;;
    -l) local=--local ;;
    --max-pack-size|--window|--window-memory|--depth)
        extra="$extra $1=$2"; shift ;;
    --) shift; break;;
    *)  usage ;;
    esac
    shift
done

Notez que cela vous permet de vérifier les drapeaux courts et longs. Dans ce cas, d'autres options sont créées à l'aide de la variable extra.

10
Kaleb Pederson

Vous pouvez utiliser le mot clé getopt dans bash.

De http://aplawrence.com/Unix/getopts.html :

getopt

Il s'agit d'un exécutable autonome qui existe depuis longtemps. Les versions plus anciennes n'ont pas la capacité de gérer les arguments cités (foo un "ça ne marchera pas" c) et les versions qui le peuvent, le font maladroitement. Si vous utilisez une version récente de Linux, votre "getopt" peut le faire; SCO OSR5, Mac OS X 10.2.6 et FreeBSD 4.4 a une ancienne version qui n'en a pas.

L'utilisation simple de "getopt" est montrée dans ce mini-script:

#!/bin/bash
echo "Before getopt"
for i
do
  echo $i
done
args=`getopt abc:d $*`
set -- $args
echo "After getopt"
for i
do
  echo "-->$i"
done
6
WhirlWind

vous pouvez adopter l'approche simple et parcourir les arguments pour tester chacun d'eux pour l'égalité avec un paramètre donné (par exemple -t).

mettez-le dans une fonction:

has_param() {
    local term="$1"
    shift
    for arg; do
        if [[ $arg == "$term" ]]; then
            return 0
        fi
    done
    return 1
}

… Et utilisez-le comme prédicat dans les expressions de test:

if has_param '-t' "$@"; then
    echo "yay!"
fi

if ! has_param '-t' "$1" "$2" "$wat"; then
    echo "nay..."
fi

si vous souhaitez rejeter des arguments vides, ajoutez un point de sortie en haut du corps de la boucle:

for arg; do
    if [[ -z "$arg" ]]; then
        return 2
    fi
    # ...

cela est très lisible, et ne vous donnera pas de faux positifs, comme la correspondance de modèle ou la correspondance d'expression régulière.
cela permettra également de placer des drapeaux à des positions arbitraires, par exemple, vous pouvez mettre -h à la fin de la ligne de commande (sans préciser si c'est bon ou mauvais).


mais, plus j'y réfléchissais, plus quelque chose me dérangeait.

avec une fonction, vous pouvez prendre n'importe quelle implémentation (par exemple getopts), et la réutiliser. encapsulation rulez!
mais même avec des commandes, cette force peut devenir un défaut. si vous l'utilisez encore et encore, vous analyserez tous les arguments à chaque fois.

ma tendance est de favoriser la réutilisation, mais je dois en être conscient. l'approche opposée serait d'analyser ces arguments une fois en haut du script, comme vous le redoutiez, et d'éviter l'analyse répétée.
vous pouvez toujours encapsuler ce boîtier de commutation, qui peut être aussi grand que vous le décidez (vous n'avez pas à lister tous les options).

4
Eliran Malka