Comment écrire un script qui fonctionne avec ou sans arguments?

13

J'ai un script bash qui ressemble à ceci:

#!/bin/bash

if [ $1 = "--test" ] || [ $1 = "-t" ]; then
    echo "Testing..."
    testing="y"

else
    testing="n"
    echo "Not testing."

fi

Donc, ce que je veux pouvoir faire, c'est non seulement l'exécuter avec ./script --testou ./script -t, mais aussi sans argument (juste ./script), mais apparemment si je le fais avec le code actuel, la sortie est juste:

./script: line 3: [: =: unary operator expected
./script: line 3: [: =: unary operator expected
Not testing.

Alors, comment puis-je le programmer pour que le faire tourner sans aucun argument fasse juste le elsesans lancer l'erreur? Qu'est-ce que je fais mal?

Peter Cordes
la source
1
Vous avez besoin de doubles crochets autour de vos chèques. [[]]
Terrance
3
Voir l' écueil Bash # 4
Steeldriver
@Terrance vous n'en avez pas besoin , mais vous devriez les utiliser si vous ciblez bash.
Ruslan

Réponses:

1

La "bonne façon" d'utiliser les options longues dans les scripts shell est via l' utilitaire getopt GNU . Il y a aussi getopts qui est un bash intégré , mais il ne permet que des options courtes comme-t . Quelques exemples d' getoptutilisation sont disponibles ici .

Voici un script qui montre comment j'aborderais votre question. L'explication de la plupart des étapes est ajoutée en tant que commentaires dans le script lui-même.

#!/bin/bash

# GNU getopt allows long options. Letters in -o correspond to
# comma-separated list given in --long.

opts=$(getopt -o t --long test -- "$*")
test $? -ne 0 && exit 2 # error happened

set -- $opts # some would prefer using eval set -- "$opts"
# if theres -- as first argument, the script is called without
# option flags
if [ "$1" = "--"  ]; then
    echo "Not testing"
    testing="n"
    # Here we exit, and avoid ever getting to argument parsing loop
    # A more practical case would be to call a function here
    # that performs for no options case
    exit 1
fi

# Although this question asks only for one 
# option flag, the proper use of getopt is with while loop
# That's why it's done so - to show proper form.
while true; do
    case "$1" in
        # spaces are important in the case definition
        -t | --test ) testing="y"; echo "Testing" ;;
    esac
    # Loop will terminate if there's no more  
    # positional parameters to shift
    shift  || break
done

echo "Out of loop"

Avec quelques simplifications et commentaires supprimés, cela peut être condensé en:

#!/bin/bash
opts=$(getopt -o t --long test -- "$*")
test $? -ne 0 && exit 2 # error happened
set -- $opts    
case "$1" in
    -t | --test ) testing="y"; echo "Testing";;
    --) testing="n"; echo "Not testing";;
esac
Sergiy Kolodyazhnyy
la source
16

Plusieurs façons; les deux plus évidents sont:

  • Mettez $1entre guillemets:if [ "$1" = "--test" ]
  • Vérifiez le nombre d'arguments à l'aide de $ #

Mieux encore, utilisez getopts.

JayEye
la source
2
+1 pour l'utilisation de getopts. C'est la meilleure façon de procéder.
Seth
3
Un autre idiome courant consiste [ "x$1" = "x--test" ]à défendre à nouveau les arguments de ligne de commande qui sont des opérateurs valides pour la [commande. (C'est une autre raison pour laquelle il [[ ]]est préférable: cela fait partie de la syntaxe du shell, pas seulement une commande intégrée.)
Peter Cordes
7

Vous devez citer vos variables à l'intérieur de la condition if. Remplacer:

if [ $1 = "--test" ] || [ $1 = "-t" ]; then

avec:

if [ "$1" = '--test' ] || [ "$1" = '-t' ]; then  

Ensuite, cela fonctionnera:

➜ ~ ./test.sh
Pas de test.

Citez toujours toujours vos variables!

Seth
la source
Les guillemets simples sont-ils essentiels ou peut-on plutôt utiliser des guillemets doubles?
La réponse précise dépend du shell que vous utilisez, mais à partir de votre question, je suppose que vous utilisez un descendant Bourne-shell. La principale différence entre les guillemets simples et doubles est que l'expansion des variables se produit à l'intérieur des guillemets doubles, mais pas à l'intérieur des guillemets simples: $ FOO = bar $ echo "$ FOO" bar $ echo '$ FOO' $ FOO (hmm .... can ' code de format t dans les commentaires ...)
JayEye
@ParanoidPanda Vous n'avez pas à utiliser de guillemets simples, non, mais il est recommandé de les utiliser sur des littéraux de chaîne. De cette façon, vous ne serez pas surpris plus tard si vos données de chaîne ont un symbole bash veut se développer.
Seth
@ParanoidPanda Qu'est - ce @JayEye dit: Avec des guillemets simples , il n'y a pas d' expansion variables foo=bar echo '$foo'imprime $fooà la sortie, mais des foo=bar echo "$foo"impressions au barlieu.
EKons
4

Vous utilisez bash. Bash a une excellente alternative à [: [[. Avec [[, vous n'avez pas à vous soucier de la cotation, et vous obtenez beaucoup plus d'opérateurs que [, y compris un support approprié pour ||:

if [[ $1 = "--test" || $1 = "-t" ]]
then
    echo "Testing..."
    testing="y"
else
    testing="n"
    echo "Not testing."
fi

Ou vous pouvez utiliser des expressions régulières:

if [[ $1 =~ ^(--test|-t) ]]
then
    echo "Testing..."
    testing="y"
else
    testing="n"
    echo "Not testing."
fi

Bien sûr, des citations appropriées doivent toujours être faites, mais il n'y a aucune raison de s'en tenir à l' [utilisation de bash.

muru
la source
3

Pour tester si des arguments sont présents (en général, et effectuez les actions en conséquence) Cela devrait fonctionner:

#!/bin/bash

check=$1

if [ -z "$check" ]; then
  echo "no arguments"
else echo "there was an argument"
fi
Jacob Vlijm
la source
Pourquoi pas [ -z "$1" ]?
EKons
@ ΈρικΚωνσταντόπουλος Dans ce cas, bien sûr, normalement cependant dans les scripts, j'appelle la variable une seule fois, je la fais référence dans les procédures, ce qui est généralement plus d'une fois. Décidé de conserver le protocole dans l'échantillon.
Jacob Vlijm
Dans cet exemple, vous semblez utiliser $checkune fois, avant tout shifts, il serait donc préférable de l'utiliser à la $1place.
EKons
@ ΈρικΚωνσταντόπουλος Bien sûr que non, comme mentionné, c'est un échantillon , à utiliser dans les scripts. Dans les scripts, c'est une mauvaise pratique de répéter la même chose encore et encore.
Jacob Vlijm
Je comprends ce que vous voulez dire, mais il est préférable de ne pas utiliser de shebangs #!lors de la fourniture d'échantillons (vous devez décrire le shell en dehors du code).
EKons