Vérifier l'existence d'un argument d'entrée dans un script shell Bash


1342

J'ai besoin de vérifier l'existence d'un argument d'entrée. J'ai le script suivant

if [ "$1" -gt "-1" ]
  then echo hi
fi

Je reçois

[: : integer expression expected

Comment vérifier d'abord l'argument d'entrée1 pour voir s'il existe?

Réponses:


2334

C'est:

if [ $# -eq 0 ]
  then
    echo "No arguments supplied"
fi

La $#variable vous indiquera le nombre d'arguments d'entrée auxquels le script a été transmis.

Ou vous pouvez vérifier si un argument est une chaîne vide ou non:

if [ -z "$1" ]
  then
    echo "No argument supplied"
fi

Le -zcommutateur testera si l'expansion de "$ 1" est une chaîne nulle ou non. S'il s'agit d'une chaîne nulle, le corps est exécuté.


62
J'aime le faire de cette façon, en syntaxe laconique et toujours POSIX acceptable. [ -z "$1" ] && echo "No argument supplied" Je préfère les monolignes, car elles sont plus faciles pour moi; et il est également plus rapide de vérifier la valeur de sortie par rapport à l'utilisationif
JM Becker

168
Vous voudrez probablement ajouter un exit 1à la fin de vos échos à l'intérieur du bloc if lorsque l'argument est requis pour que le script fonctionne. Évident, mais mérite d'être noté pour être complet.
msanford

16
Il est possible, bien que rarement utile, que le premier argument soit initialisé mais vide; programname "" secondarg third. La $#vérification vérifie sans ambiguïté le nombre d'arguments.
tripleee

39
Pour un noob, en particulier quelqu'un qui vient d'un milieu non scripteur, il est également important de mentionner certaines particularités de ces choses. Vous auriez également pu mentionner que nous avons besoin d'un espace après l'ouverture et le croisillon de fermeture. Sinon, les choses ne fonctionnent pas. Je suis moi-même un noob scripteur (je viens du milieu C) et je l'ai trouvé à la dure. Ce n'est que lorsque j'ai décidé de copier le tout "tel quel" que les choses ont fonctionné pour moi. C'est alors que j'ai réalisé que je devais quitter un espace après l'accolade d'ouverture et avant la fermeture.
HighOnMeat

72
et pour les if [ ! -z "$1" ]; then ...
arguments

347

Il vaut mieux démontrer de cette façon

if [[ $# -eq 0 ]] ; then
    echo 'some message'
    exit 1
fi

Vous devez normalement quitter si vous avez trop peu d'arguments.


72
Non, ce n'est pas le cas: c'est ce exit 1que vous voulez habituellement et utilise le [[ ]]test qui (iirc) est généralement plus raisonnable. Donc, pour les gens, copier-coller aveuglément du code est la meilleure réponse.
dshepherd

42
Pour en savoir plus sur la différence entre [] et [[]], voir stackoverflow.com/questions/3427872/…
Sebastián Grignoli

104

Dans certains cas, vous devez vérifier si l'utilisateur a passé un argument au script et sinon, revenir à une valeur par défaut. Comme dans le script ci-dessous:

scale=${2:-1}
emulator @$1 -scale $scale

Ici, si l'utilisateur n'a pas passé scalecomme 2ème paramètre, je lance l'émulateur Android -scale 1par défaut. ${varname:-word}est un opérateur d'expansion. Il existe également d'autres opérateurs d'expansion:

  • ${varname:=word}qui définit l'indéfini varnameau lieu de renvoyer la wordvaleur;
  • ${varname:?message}qui retourne varnames'il est défini et n'est pas nul ou imprime le messageet abandonne le script (comme le premier exemple);
  • ${varname:+word}qui wordne renvoie que si varnameest défini et n'est pas nul; renvoie null sinon.

1
L'exemple ci-dessus semble utiliser ${varname?message}. Le supplément est-il :une faute de frappe ou change-t-il de comportement?
Eki

6
Eki, le ":" est une commande intégrée et un raccourci pour / bin / true dans cet exemple. Il représente une commande de ne rien faire qui ignore fondamentalement les arguments qui lui sont fournis. Il est essentiel dans ce test pour empêcher l'interpréteur d'essayer d'exécuter le contenu de "$ varname" (ce que vous ne voulez certainement PAS voir se produire). A noter également; vous pouvez tester autant de variables que vous le souhaitez avec cette méthode. Et le tout avec des messages d'erreur spécifiques. ie: ${1?"First argument is null"} ${2?"Please provide more than 1 argument"}
user.friendly

48

Essayer:

 #!/bin/bash
 if [ "$#" -eq  "0" ]
   then
     echo "No arguments supplied"
 else
     echo "Hello world"
 fi

4
Pourquoi avez-vous besoin de guillemets doubles pour $#et 0?
user13107

1
Pas de problème si nous utilisons sans guillemets comme $ # et 0
Ranjithkumar T

sur windows, mingw c'est le seul chemin à parcourir.
Lajos Meszaros

2
Cette réponse fournit un excellent point de départ pour un script que je viens de créer. Merci d'avoir montré le elseaussi.
Chris K

2
@ user13107 les variables entre guillemets doubles dans bash empêchent la globalisation (c'est-à-dire l'expansion des noms de fichiers comme foo*) et le fractionnement des mots (c'est-à-dire le fractionnement du contenu si la valeur contient des espaces). Dans ce cas, il n'est pas nécessaire de citer $#car ces deux cas ne s'appliquent pas. Il 0n'est pas non plus nécessaire de citer le, mais certaines personnes préfèrent citer des valeurs car ce sont vraiment des chaînes et cela le rend plus explicite.
Dennis

39

Une autre façon de détecter si des arguments ont été passés au script:

((!$#)) && echo No arguments supplied!

Notez que (( expr ))l'expression est évaluée selon les règles de Shell Arithmetic .

Pour sortir en l'absence d'arguments, on peut dire:

((!$#)) && echo No arguments supplied! && exit 1

Une autre façon (analogue) de dire ce qui précède serait:

let $# || echo No arguments supplied

let $# || { echo No arguments supplied; exit 1; }  # Exit if no arguments!

help let dit:

let: let arg [arg ...]

  Evaluate arithmetic expressions.

  ...

  Exit Status:
  If the last ARG evaluates to 0, let returns 1; let returns 0 otherwise.

2
-1 ce pourrait être la pire méthode si la validation de l'existence d'un argument .. plus elle peut déclencher une substitution d'historique et potentiellement faire de mauvaises choses.
user.friendly

2
au lieu de exitqui tue mon processus zsh, j'utilise returnce qui ne le tue pas
Timo

Pourquoi ((!$#))déclencherait une substitution de l'histoire?
Zhro

25

J'utilise souvent cet extrait de code pour des scripts simples:

#!/bin/bash

if [ -z "$1" ]; then
    echo -e "\nPlease call '$0 <argument>' to run this command!\n"
    exit 1
fi

1
Donc, cela doit être utilisé si vous n'avez besoin que d'un seul argument?
Danijel

21

Seulement parce qu'il y a un point de base plus à souligner, j'ajouterai que vous pouvez simplement tester que votre chaîne est nulle:

if [ "$1" ]; then
  echo yes
else
  echo no
fi

De même, si vous attendez le nombre d'arguments, testez votre dernier:

if [ "$3" ]; then
  echo has args correct or not
else
  echo fixme
fi

et ainsi de suite avec tout arg ou var


4

Si vous souhaitez vérifier si l'argument existe, vous pouvez vérifier si le nombre d'arguments est supérieur ou égal à votre numéro d'argument cible.

Le script suivant montre comment cela fonctionne

test.sh

#!/usr/bin/env bash

if [ $# -ge 3 ]
then
  echo script has at least 3 arguments
fi

produit la sortie suivante

$ ./test.sh
~
$ ./test.sh 1
~
$ ./test.sh 1 2
~
$ ./test.sh 1 2 3
script has at least 3 arguments
$ ./test.sh 1 2 3 4
script has at least 3 arguments

3

En tant que petit rappel, les opérateurs de test numériques dans Bash ne fonctionnent que sur des entiers ( -eq, -lt,-ge , etc.)

J'aime m'assurer que mes $ vars sont des pouces par

var=$(( var + 0 ))

avant de les tester, juste pour me défendre contre l'erreur "[: arg entier requis".


1
Astuce intéressante, mais veuillez noter: en raison de l'incapacité de bash à gérer les flottants en arithmétique, cette méthode peut provoquer une erreur de syntaxe et retourner non nul, ce qui serait un obstacle lorsque l'errexit est activé. var=$(printf "%.0f" "$var")peut gérer les flottants mais souffre de la sortie non nulle quand on lui donne une chaîne. Si cela ne vous dérange pas awk, cette méthode utilisation I semble être le plus robuste pour faire respecter un entier: var=$(<<<"$var" awk '{printf "%.0f", $0}'). Si var n'est pas défini, il est par défaut "0". Si var est un flottant, il est arrondi à l'entier le plus proche. Les valeurs négatives sont également très bien à utiliser.
user.friendly

0

une validation de fonction bash liner

myFunction() {

    : ${1?"forgot to supply an argument"}
    if [ "$1" -gt "-1" ]; then
        echo hi
    fi

}

ajouter le nom et l'utilisation de la fonction

myFunction() {

    : ${1?"forgot to supply an argument ${FUNCNAME[0]}() Usage:  ${FUNCNAME[0]} some_integer"}
    if [ "$1" -gt "-1" ]; then
        echo hi
    fi

}

ajouter une validation pour vérifier si un entier

pour ajouter une validation supplémentaire, par exemple pour vérifier si l'argument passé est un entier, modifiez la validation d'une ligne pour appeler une fonction de validation:

: ${1?"forgot to supply an argument ${FUNCNAME[0]}() Usage:  ${FUNCNAME[0]} some_integer"} && validateIntegers $1 || die "Must supply an integer!"

puis, construisez une fonction de validation qui valide l'argument, renvoyant 0 en cas de succès, 1 en cas d'échec et une fonction die qui abandonne le script en cas d'échec

validateIntegers() {

    if ! [[ "$1" =~ ^[0-9]+$ ]]; then
        return 1 # failure
    fi
    return 0 #success

}

die() { echo "$*" 1>&2 ; exit 1; }

Encore plus simple - utilisez simplement set -u

set -u s'assure que chaque variable référencée est définie lors de son utilisation, il suffit donc de la définir et de l'oublier

myFunction() {
    set -u
    if [ "$1" -gt "-1" ]; then
        echo hi
    fi

}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.