Pourquoi ne pas utiliser plusieurs commandes avec un || ou && travail conditionnel?


12

Cela fonctionne sur une invite shell (bash, dash):

[ -z "" ] && echo A || echo B
A

Cependant, j'essaie d'écrire un script shell POSIX , cela commence comme ceci:

#!/bin/sh

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; exit 1

readonly raw_input_string=${1}

[ -z "${raw_input_string}" ] && echo "The given argument is empty."; exit 1

Et je ne sais pas pourquoi, mais je ne reçois pas le message :

L'argument donné est vide.

si j'appelle le script comme ceci:

./test_empty_argument ""

Pourquoi donc?


5
Voir Comment puis-je tester si une variable est vide ou contient uniquement des espaces? pour savoir comment tester si une variable est vide, non définie ou ne contient que des blancs. Le problème dans cette question n'a rien à voir avec cela.
ilkkachu

1
Il suffit d'utiliserif [ X”” = X”$var” ] ; then echo isempty ; fi
user2497

3
@ user2497 Il n'y a aucune raison de l'utiliser dans un shell sorti au cours des 20 dernières années. C'est une solution de contournement pour les vieilles coquilles de buggy.
chepner

@chepner Ce n'est donc pas une solution valable? Quelque chose d'autre doit être utilisé?
user2497

6
[ "" = "$var" ]fonctionnerait bien; une chaîne vide entre guillemets ne sera pas supprimée de la liste d'arguments de [. Mais ce n'est pas nécessaire non plus, car cela fonctionne [ -z "$var" ] aussi très bien.
chepner

Réponses:


37

Notez que votre ligne

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; exit 1

c'est la même chose que

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."
exit 1

(un caractère non cité ;peut, dans la plupart des cas, être remplacé par un caractère de nouvelle ligne)

Cela signifie que l' exit 1instruction est toujours exécutée quel que soit le nombre d'arguments transmis au script. Cela signifie à son tour que le message The given argument is empty.n'aura jamais de chance d'être imprimé.

Pour exécuter plusieurs instructions après un test utilisant la "syntaxe de court-circuit", regroupez les instructions dans { ...; }. L'alternative consiste à utiliser une ifdéclaration appropriée (qui, à mon humble avis, semble plus propre dans un script):

if [ "$#" -ne 1 ]; then
    echo 'Invalid number of arguments, expected one.' >&2
    exit 1
fi

Vous avez le même problème avec votre deuxième test.


En ce qui concerne

[ -z "" ] && echo A || echo B

Cela fonctionnerait pour l'exemple donné, mais le générique

some-test && command1 || command2

ne serait pas le même que

if some-test; then
    command1
else
    command2
fi

Au lieu de cela, cela ressemble plus à

if ! { some-test && command1; }; then
    command2
fi

ou

if some-test && command1; then
    :
else
    command2
fi

Autrement dit, si le test ou la première commande échoue, la deuxième commande s'exécute, ce qui signifie qu'elle a le potentiel pour exécuter les trois instructions impliquées.


18

Cette:

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; exit 1

n'est pas:

[ "${#}" -eq 1 ] || { echo "Invalid number of arguments, expected one."; exit 1; }

Mais c'est plutôt:

{ [ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; } 
exit 1

Votre script se termine quel que soit le nombre d'arguments que vous lui avez transmis.


8

Une façon de le rendre plus lisible est de définir une diefonction (à la perl) comme:

die() {
  printf >&2 '%s\n' "$@"
  exit 1
}

# then:

[ "$#" -eq 1 ] || die "Expected one argument, got $#"

[ -n "$1" ] || die "Empty argument not supported"

Vous pouvez ajouter plus de cloches et de sifflets comme les couleurs, le préfixe, le numéro de ligne ... si besoin est.


En pratique, appelez-vous jamais votre diefonction avec plusieurs arguments? (Si c'est le cas, pouvez-vous donner un exemple?) J'utilise une diefonction presque identique , mais j'utilise à la "$*"place, ce qui peut être plus ce que vous envisagez?
jrw32982 prend en charge Monica le

3
La valeur de "$@"est qu'il autorise les messages sur plusieurs lignes sans avoir besoin d'ajouter de nouvelles lignes littérales.
Charles Duffy

1
@ jrw32982, utiliser "$*"pour joindre des arguments avec des espaces signifie également que vous devez définir $IFSSPC pour qu'il fonctionne dans tous les contextes, y compris ceux où il $IFSa été modifié. Alternativement avec ksh/ zsh, vous pouvez utiliser print -r -- "$@"ou echo -E - "$@"dans zsh.
Stéphane Chazelas

@CharlesDuffy Oui, mais je n'ai jamais vu cela se faire dans le contexte d'une diefonction de type. Ce que je demande, c'est: dans la pratique, avez-vous déjà vu quelqu'un écrire die "unable to blah:" "some error", dans le but d'obtenir un message d'erreur de 2 lignes?
jrw32982 prend en charge Monica

@ StéphaneChazelas Bon point. Donc (dans ma formulation) ça devrait être die() { IFS=" "; printf >&2 "%s\n" "$*"; exit 1; }. Avez-vous déjà personnellement utilisé ce type de diefonction pour printfgénérer un message d'erreur multiligne en passant plusieurs arguments? Ou ne passez-vous jamais qu'un seul argument à dieafin qu'il n'ajoute qu'une seule nouvelle ligne à sa sortie?
jrw32982 prend en charge Monica

-1

J'ai souvent vu cela comme un test pour une chaîne vide:

if [ "x$foo" = "x" ]; then ...

Devrait avoir été "=" - fixe.
wef

3
Cette pratique date littéralement des années 1970. Il n'y a aucune raison de l' utiliser avec une coque qui est conforme à la norme sh Posix 1992 (aussi longtemps que correcte citant est utilisé et la fonctionnalité maintenant obsolescents tels que -a, -o, (et )que derectives à dire testde combiner plusieurs opérations en une seule invocation sont évités; voir les marqueurs OB dans pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html ).
Charles Duffy

Unices non-linux livrés avec le 'sh' d'origine bien dans les années 90 - peut-être encore. Dans mon travail à l'époque, j'ai dû écrire des scripts d'installation portables et j'ai utilisé cette construction. Je viens de regarder les scripts d'installation que NVidia expédie pour linux et ils utilisent toujours cette construction.
wef

NVidia peut l'utiliser, mais cela ne veut pas dire qu'ils ont une justification technique pour le faire; le développement du cargo-culte dans UNIX commercial est malheureusement répandu. Même Heirloom Bourne n'a pas le bogue en question - donc la base de code SunOS (qui est le dernier UNIX commercial à expédier un non-POSIX /bin/sh, et le prédécesseur immédiat de Heirloom Bourne) ne l'avait pas non plus.
Charles Duffy

1
Je ne porte pas de chapeau, donc je ne peux pas promettre de publier une vidéo YouTube en mangeant si quelqu'un montait un shell publié sur un UNIX commercial avec ce bug après 1990 ... mais si je le faisais, ce serait tentant . :)
Charles Duffy
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.