Vérifier le nombre d'arguments passés à un script Bash

727

Je voudrais que mon script Bash affiche un message d'erreur si le nombre d'arguments requis n'est pas atteint.

J'ai essayé le code suivant:

#!/bin/bash
echo Script name: $0
echo $# arguments 
if [$# -ne 1]; 
    then echo "illegal number of parameters"
fi

Pour une raison inconnue, j'ai l'erreur suivante:

test: line 4: [2: command not found

Qu'est-ce que je fais mal?

Naftaly
la source
54
Vous ne devez pas nommer votre script test. C'est le nom d'une commande Unix standard, vous ne voudriez pas la masquer.
Barmar
20
Utilisez toujours des espaces autour de '[' ('[[') ou '(' ('((()) dans les instructions if en bash.
zoska
5
Pour ajouter au commentaire @zoska, vous avez besoin d'un espace avant [car il est implémenté comme une commande, essayez 'which ['.
Daniel Da Cunha
1
un meilleur exemple est donné sur le lien ci-dessous: stackoverflow.com/questions/4341630/…
ramkrishna
3
@Barmar le nommant sûrement, c'est testbien tant qu'il n'est pas sur le CHEMIN?
user253751

Réponses:

1099

Comme toute autre commande simple, [ ... ]ou testnécessite des espaces entre ses arguments.

if [ "$#" -ne 1 ]; then
    echo "Illegal number of parameters"
fi

Ou

if test "$#" -ne 1; then
    echo "Illegal number of parameters"
fi

Suggestions

Lorsque vous êtes dans Bash, préférez utiliser à la [[ ]]place car il ne fait pas de fractionnement de mot et d'expansion de nom de chemin à ses variables que la citation peut ne pas être nécessaire sauf si elle fait partie d'une expression.

[[ $# -ne 1 ]]

Il possède également d'autres fonctionnalités telles que le regroupement de conditions sans guillemets, la correspondance de modèles (correspondance de modèles étendue avec extglob) et la correspondance d'expressions régulières.

L'exemple suivant vérifie si les arguments sont valides. Il permet un seul argument ou deux.

[[ ($# -eq 1 || ($# -eq 2 && $2 == <glob pattern>)) && $1 =~ <regex pattern> ]]

Pour pures expressions arithmétiques, en utilisant (( ))à certains peuvent encore être mieux, mais ils sont encore possibles dans [[ ]]ses opérateurs arithmétiques comme -eq, -ne, -lt, -le, -gtou -geen plaçant l'expression comme un seul argument de chaîne:

A=1
[[ 'A + 1' -eq 2 ]] && echo true  ## Prints true.

Cela devrait être utile si vous devez également le combiner avec d'autres fonctionnalités de [[ ]].

Quitter le script

Il est également logique de quitter le script lorsque des paramètres non valides lui sont transmis. Cela a déjà été suggéré dans les commentaires par ekangas mais quelqu'un a édité cette réponse pour l'avoir -1comme valeur retournée, donc je ferais aussi bien de le faire correctement.

-1bien qu'accepté par Bash comme argument, il exitn'est pas explicitement documenté et ne peut être utilisé comme suggestion courante. 64est également la valeur la plus formelle car elle est définie sysexits.havec #define EX_USAGE 64 /* command line usage error */. La plupart des outils comme lsreviennent également 2sur des arguments invalides. J'avais aussi l'habitude de revenir 2dans mes scripts mais dernièrement je ne m'en souciais plus vraiment, et j'utilisais simplement 1dans toutes les erreurs. Mais plaçons-nous 2ici car c'est le plus courant et probablement pas spécifique au système d'exploitation.

if [[ $# -ne 1 ]]; then
    echo "Illegal number of parameters"
    exit 2
fi

Références

konsolebox
la source
2
OP: Gardez à l'esprit qu'il ne [s'agit que d'une autre commande, c'est-à-dire, essayez which [.
Leo
5
Les commandes @Leo peuvent être intégrées et ne peuvent pas l'être. En bash, [est un intégré, tandis que [[est un mot-clé. Dans certains obus plus anciens, [n'est même pas intégré. Des commandes comme [naturellement coexistent en tant que commande externe dans la plupart des systèmes, mais les commandes internes sont priorisées par le shell, sauf si vous contournez avec commandou exec. Consultez la documentation du shell sur la façon dont ils évaluent. Prenez note de leur différence et de la façon dont ils peuvent se comporter différemment dans chaque coquille.
konsolebox
78

Il pourrait être judicieux d'utiliser des expressions arithmétiques si vous avez affaire à des nombres.

if (( $# != 1 )); then
    echo "Illegal number of parameters"
fi
Aleks-Daniel Jakimenko-A.
la source
Pourquoi cela pourrait-il être une bonne idée, dans le cas présent? Compte tenu de l'efficacité, de la portabilité et d'autres problèmes, n'est-il pas préférable d'utiliser la syntaxe la plus simple et la plus universellement comprise, c'est-à-dire [ ... ]lorsque cela fonctionne bien et qu'aucune opération de fantaisie n'est nécessaire?
Max
Les extensions arithmétiques @Max $(( ))ne sont pas fantaisistes et devraient être implémentées par tous les shells POSIX. Cependant, la (( ))syntaxe (sans $) n'en fait pas partie. Si vous êtes limité pour une raison quelconque, vous pouvez certainement l'utiliser à la [ ]place, mais gardez à l'esprit que vous ne devriez pas l'utiliser [[ ]]également. J'espère que vous comprenez les pièges [ ]et les raisons pour lesquelles ces fonctionnalités existent. Mais c'était une question Bash donc nous donnons des réponses Bash ( "En règle générale, [[est utilisé pour les chaînes et les fichiers. Si vous voulez comparer des nombres, utilisez une ArithmeticExpression" ).
Aleks-Daniel Jakimenko-A.
40

Sur [] :! =, =, == ... sont des opérateurs de comparaison de chaînes et -eq, -gt ... sont des opérateurs binaires arithmétiques .

J'utiliserais:

if [ "$#" != "1" ]; then

Ou:

if [ $# -eq 1 ]; then
jhvaras
la source
9
==est en fait une fonctionnalité non documentée, qui arrive à travailler avec GNU test. Il a également produit de travailler avec FreeBSD test, mais peut ne pas fonctionner sur foo test . La seule comparaison standard est =(juste FYI).
Martin Tournoij
1
Il est documenté sur l'entrée bash man: Lorsque les opérateurs == et! = Sont utilisés, la chaîne à droite de l'opérateur est considérée comme un modèle et mise en correspondance selon les règles décrites ci-dessous sous Correspondance de modèle. Si l'option shell nocasematch est activée, la correspondance est effectuée sans tenir compte de la casse des caractères alphabétiques. La valeur de retour est 0 si la chaîne correspond à (==) ou ne correspond pas (! =) Au modèle, et 1 sinon. N'importe quelle partie du modèle peut être citée pour le forcer à correspondre en tant que chaîne.
jhvaras
2
@jhvaras: C'est exactement ce que Carpetsmoker a dit: cela peut fonctionner dans certaines implémentations (et en effet, cela fonctionne dans Bash), mais il n'est pas compatible POSIX . Par exemple, il échouera avec dash: dash -c '[ 1 == 1 ]'. POSIX spécifie seulement =, et non ==.
gniourf_gniourf
34

Si vous n'êtes intéressé par le baillage que si un argument particulier est manquant, la substitution de paramètres est idéale:

#!/bin/bash
# usage-message.sh

: ${1?"Usage: $0 ARGUMENT"}
#  Script exits here if command-line parameter absent,
#+ with following error message.
#    usage-message.sh: 1: Usage: usage-message.sh ARGUMENT
Tapoter
la source
n'est-ce pas chargé de bashismes?
Dwight Spencer
@DwightSpencer Serait-ce important?
konsolebox
@Temak Je peux si vous avez des questions spécifiques, mais l'article lié l'explique mieux que moi.
Pat
13

Un simple liner qui fonctionne peut être fait en utilisant:

[ "$#" -ne 1 ] && ( usage && exit 1 ) || main

Cela se décompose en:

  1. tester la variable bash pour la taille des paramètres $ # n'est pas égal à 1 (notre nombre de sous-commandes)
  2. si vrai, appelez la fonction use () et quittez avec le statut 1
  3. sinon, appelez la fonction main ()

Pense à noter:

  • usage () peut être simplement un écho "$ 0: params"
  • main peut être un long script
Dwight Spencer
la source
1
Si vous avez un autre ensemble de lignes après cette ligne, ce serait faux, car exit 1cela ne s'appliquerait qu'au contexte de la sous-coque, ce qui la rend également synonyme de ( usage; false ). Je ne suis pas fan de cette manière de simplifier en ce qui concerne l'analyse des options, mais vous pouvez utiliser à la { usage && exit 1; }place. Ou probablement juste { usage; exit 1; }.
konsolebox
1
@konsolebox (usage && exit 1) fonctionne pour ksh, zsh et bash pour revenir à bash 2.0. La syntaxe {...} n'est récente qu'à 4.0+ de bash. Ne vous méprenez pas si une façon fonctionne bien pour vous, alors utilisez-la, mais n'oubliez pas que tout le monde n'utilise pas la même implémentation de bash que vous et nous devrions coder pour poser des normes et non des bashismes.
Dwight Spencer
Je ne sais pas trop ce que tu dis. {...}est une syntaxe commune et est disponible pour la plupart sinon tous les shells basés sur sh, même les shells plus anciens ne suivant pas les standards POSIX.
konsolebox
7

Découvrez cette feuille de triche bash, il peut aider beaucoup.

Pour vérifier la longueur des arguments transmis, vous utilisez "$#"

Pour utiliser le tableau d'arguments transmis, vous utilisez "$@"

Un exemple de vérification de la longueur et d'itération serait:

myFunc() {
  if [[ "$#" -gt 0 ]]; then
    for arg in "$@"; do
      echo $arg
    done
  fi
}

myFunc "$@"

Ce stage m'a aidé, mais il manquait quelques choses pour moi et ma situation. J'espère que cela aide quelqu'un.

Nick Hall
la source
0

Au cas où vous voudriez être du bon côté, je recommande d'utiliser des getopts.

Voici un petit exemple:

    while getopts "x:c" opt; do
      case $opt in
        c)
          echo "-$opt was triggered, deploy to ci account" >&2
          DEPLOY_CI_ACCT="true"
          ;;
            x)
              echo "-$opt was triggered, Parameter: $OPTARG" >&2 
              CMD_TO_EXEC=${OPTARG}
              ;;
            \?)
              echo "Invalid option: -$OPTARG" >&2 
              Usage
              exit 1
              ;;
            :)
              echo "Option -$OPTARG requires an argument." >&2 
              Usage
              exit 1
              ;;
          esac
        done

voir plus de détails ici par exemple http://wiki.bash-hackers.org/howto/getopts_tutorial

IsaacE
la source
0

Voici un simple liners pour vérifier si un seul paramètre est donné sinon quittez le script:

[ "$#" -ne 1 ] && echo "USAGE $0 <PARAMETER>" && exit
panticz
la source
-1

Vous devez ajouter des espaces entre les conditions de test:

if [ $# -ne 1 ]; 
    then echo "illegal number of parameters"
fi

J'espère que ça aide.

Fabricio
la source