987 votes

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

Je voudrais que mon script Bash imprime 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 obtenu l'erreur suivante :

test: line 4: [2: command not found

Qu'est-ce que je fais de mal ?

75 votes

Vous ne devriez pas nommer votre script test . C'est le nom d'une commande Unix standard, vous ne voudriez pas la cacher.

29 votes

Utilisez toujours des espaces autour de '[' ('[[') ou '(' ('((')) dans les instructions if en bash.

6 votes

Pour ajouter au commentaire de @zoska, vous avez besoin d'un espace avant [ car il est implémenté comme une commande, essayez 'which ['.

1505voto

konsolebox Points 21338

Comme toute autre commande simple, [ ... ] o test requiert 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, puisqu'il n'effectue pas le découpage des mots et l'expansion des noms de chemin pour ses variables, la citation peut ne pas être nécessaire, sauf si elle fait partie d'une expression.

[[ $# -ne 1 ]]

Il dispose également d'autres fonctionnalités telles que le regroupement de conditions non citées, le filtrage de motifs (filtrage de motifs étendu avec extglob ) et la correspondance regex.

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

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

Pour les expressions arithmétiques pures, l'utilisation de (( )) pour certains peuvent encore être meilleures, mais elles sont toujours possibles en [[ ]] avec ses opérateurs arithmétiques comme -eq , -ne , -lt , -le , -gt o -ge en plaçant l'expression comme un seul argument de type chaîne :

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

Cela devrait être utile si vous avez besoin de le combiner avec d'autres fonctionnalités de l'outil de gestion de l'information. [[ ]] également.

Notez que [[ ]] y (( )) sont des mots-clés qui ont le même niveau d'analyse syntaxique que if , case , while y for .

Aussi comme Dave suggéré, les messages d'erreur sont mieux envoyés à stderr afin qu'ils ne soient pas inclus lorsque stdout est redirigé :

echo "Illegal number of parameters" >&2

Sortie du script

Il est également logique de faire en sorte que le script sorte lorsque des paramètres invalides lui sont passés. Cela a déjà été suggéré dans le commentaires por ekangas mais quelqu'un a édité cette réponse pour l'avoir avec -1 comme valeur retournée, alors autant le faire correctement.

-1 bien qu'acceptée par Bash comme un argument pour exit n'est pas explicitement documenté et ne doit pas être utilisé comme une suggestion commune. 64 est également la valeur la plus formelle puisqu'elle est définie dans le document sysexits.h con #define EX_USAGE 64 /* command line usage error */ . La plupart des outils comme ls retourner également 2 sur des arguments non valables. J'avais aussi l'habitude de renvoyer 2 dans mes scripts mais dernièrement je ne m'en souciais plus vraiment et j'ai simplement utilisé 1 dans toutes les erreurs. Mais plaçons 2 ici puisque c'est le plus courant et qu'il n'est probablement pas spécifique au système d'exploitation.

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

Références

2 votes

OP : Gardez à l'esprit que [ n'est qu'une autre commande, c'est-à-dire qu'essayer which [ .

7 votes

Léo Les commandes peuvent être intégrées ou non. Dans bash, [ est un buildin, tandis que [[ est un mot-clé. Dans certains anciens shells, [ n'est même pas intégré. Des commandes comme [ coexistent naturellement en tant que commande externe dans la plupart des systèmes, mais les commandes internes sont prioritaires pour l'interpréteur de commandes, à moins que vous ne les contourniez avec la commande command o exec . Vérifiez la documentation du shell sur la façon dont il évalue. Prenez note de leur différence, et du fait qu'ils peuvent se comporter différemment dans chaque shell.

1 votes

Une dernière chose, je suggérerais d'écrire le message d'erreur dans STDERR avant de sortir avec un code d'erreur. Ceci ferait l'affaire : (>&2 echo 'Illegal number of parameters')

112voto

Ce serait une bonne idée d'utiliser expressions arithmétiques si vous avez affaire à des chiffres.

if (( $# != 1 )); then
    >&2 echo "Illegal number of parameters"
fi

>&2 est utilisé pour écrire le message d'erreur sur stderr.

0 votes

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 questions, n'est-il pas préférable d'utiliser la syntaxe la plus simple et la plus universellement comprise, c'est-à-dire.., [ ... ] alors que cela fait très bien l'affaire et qu'aucune opération fantaisiste n'est nécessaire ?

3 votes

Expansions arithmétiques @Max $(( )) ne sont pas fantaisistes et devraient être implémentées par tous les shells POSIX. Cependant, les (( )) (sans $ ) n'en fait pas partie. Si, pour une raison quelconque, vous êtes limité, vous pouvez sûrement utiliser [ ] à la place, mais gardez à l'esprit qu'alors vous ne devriez pas utiliser [[ ]] aussi. J'espère que vous comprenez les pièges de [ ] et les raisons pour lesquelles ces caractéristiques existent. Mais comme il s'agissait d'une question Bash, nous donnons des réponses Bash ( ["En règle générale, [[ est utilisé pour les chaînes de caractères et les fichiers. Si vous voulez comparer des nombres, utilisez une ArithmeticExpression"](https://mywiki.wooledge.org/BashFAQ/031) ).

0 votes

En cas d'erreur, l'écriture se fait toujours sur STDERR. (>&2 echo 'Illegal number of parameters')

47voto

jhvaras Points 391

Sur [] : !=, =, == ... sont chaîne de caractères opérateurs de comparaison et -eq, -gt ... sont des arithmétique binaires.

Je l'utiliserais :

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

Ou :

if [ $# -eq 1 ]; then

11 votes

== est en fait une fonctionnalité non documentée, qui se produit pour travailler avec GNU test . Il a également se produit pour fonctionner avec FreeBSD test mais mai ne fonctionne pas sur foo test . Le site seulement La comparaison standard est = (juste pour info).

2 votes

Il est documenté dans l'entrée man de bash : Lorsque les opérateurs == et != sont utilisés, la chaîne de caractères située à droite de l'opérateur est considérée comme un motif et fait l'objet d'une correspondance selon les règles décrites ci-dessous dans la section Correspondance de motifs. Si l'option nocasematch du shell 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 motif, et 1 sinon. N'importe quelle partie du motif peut être citée pour forcer la correspondance sous forme de chaîne.

2 votes

@jhvaras : C'est exactement ce que Carpetsmoker a dit : il mai fonctionne dans certaines implémentations (et en effet, elle fonctionne dans Bash), mais il n'est pas conforme à la norme POSIX . Par exemple, il échouer con dash : dash -c '[ 1 == 1 ]' . POSIX ne spécifie que = et non == .

37voto

Pat Points 4681

Si vous n'êtes intéressé que par le fait d'abandonner si un argument particulier est manquant, Substitution de paramètres est génial :

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

0 votes

N'est-ce pas chargé de bashismes ?

1 votes

@DwightSpencer Est-ce important ?

0 votes

@Temak Je peux le faire si vous avez des questions spécifiques, mais l'article en lien l'explique mieux que moi.

16voto

Dwight Spencer Points 188

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

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

Cela se décompose comme suit :

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

Des choses à noter :

  • usage() peut être simple echo "$0 : params".
  • main peut être un long script.

1 votes

Si vous avez une autre série de lignes après cette ligne, ce serait faux car exit 1 ne s'appliquerait qu'au contexte de la sous-coquille, ce qui en fait un simple synonyme de ( usage; false ) . Je ne suis pas un fan de cette manière de simplifier l'analyse des options, mais vous pouvez utiliser { usage && exit 1; } à la place. Ou probablement juste { usage; exit 1; } .

1 votes

@konsolebox (usage && exit 1 ) fonctionne pour ksh, zsh et bash jusqu'à bash 2.0. La syntaxe {...} n'est récente que depuis la version 4.0+ de bash. Ne vous méprenez pas, si une méthode vous convient, utilisez-la, mais rappelez-vous que tout le monde n'utilise pas la même implémentation de bash que vous et que nous devrions coder selon les standards Posix, pas selon les bashismes.

0 votes

Je ne suis pas sûr de ce que vous dites. {...} est une syntaxe commune et est disponible pour la plupart, si ce n'est tous les shells basés sur sh même les anciens shells qui ne respectent pas les normes POSIX.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X