Warum werden nicht mehrere Befehle mit einem || oder && bedingte Arbeit?

12

Dies funktioniert auf einer Shell-Eingabeaufforderung (Bash, Dash):

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

Ich versuche jedoch, ein POSIX- Shell-Skript zu schreiben. Es beginnt folgendermaßen:

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

Und ich weiß nicht warum, aber ich verstehe die Nachricht nicht :

Das angegebene Argument ist leer.

wenn ich das script so aufrufe:

./test_empty_argument ""

Warum das?

LinuxSecurityFreak
quelle
5
Siehe Wie kann ich testen, ob eine Variable leer ist oder nur Leerzeichen enthält? Hier erfahren Sie, wie Sie testen können, ob eine Variable leer oder nicht gesetzt ist oder nur Leerzeichen enthält. Das Problem in dieser Frage hat nichts damit zu tun.
Ilkkachu
1
Verwenden if [ X”” = X”$var” ] ; then echo isempty ; fi
Sie
3
@ user2497 Es gibt keinen Grund, dies in einer Shell zu verwenden, die in den letzten 20 Jahren veröffentlicht wurde. Das ist eine Problemumgehung für alte, fehlerhafte Muscheln.
Chepner
@chepner Also ist es keine gültige Lösung? Muss noch etwas verwendet werden?
user2497
6
[ "" = "$var" ]würde gut funktionieren; Eine in Anführungszeichen gesetzte leere Zeichenfolge wird nicht aus der Argumentliste von entfernt [. Aber das ist auch nicht nötig, weil es [ -z "$var" ] auch gut funktioniert.
Chepner

Antworten:

37

Beachten Sie, dass Ihre Linie

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

das ist das selbe wie

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

(Ein nicht ;zitiertes Zeichen kann in den meisten Fällen durch ein Zeilenvorschubzeichen ersetzt werden.)

Dies bedeutet, dass die exit 1Anweisung immer ausgeführt wird, unabhängig davon, wie viele Argumente an das Skript übergeben wurden. Dies bedeutet wiederum, dass die Nachricht The given argument is empty.niemals die Chance haben würde, gedruckt zu werden.

Gruppieren Sie die Anweisungen in, um nach einem Test mehr als eine einzelne Anweisung mit der "Kurzschlusssyntax" auszuführen { ...; }. Die Alternative ist die Verwendung einer richtigen ifAnweisung (die in einem Skript meiner Meinung nach sauberer aussieht):

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

Sie haben das gleiche Problem mit Ihrem zweiten Test.


Bezüglich

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

Dies würde für das gegebene Beispiel funktionieren, aber für das generische

some-test && command1 || command2

wäre nicht das gleiche wie

if some-test; then
    command1
else
    command2
fi

Stattdessen ist es eher so

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

oder

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

Das heißt, wenn entweder der Test oder der erste Befehl fehlschlägt, wird der zweite Befehl ausgeführt, was bedeutet, dass alle drei beteiligten Anweisungen ausgeführt werden können.

Kusalananda
quelle
18

Dies:

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

ist nicht:

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

Aber stattdessen ist:

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

Ihr Skript wird beendet, unabhängig davon, wie viele Argumente Sie an das Skript übergeben haben.

muru
quelle
8

Eine Möglichkeit, die Lesbarkeit zu verbessern, besteht darin, eine dieFunktion (à la perl) wie folgt zu definieren :

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

# then:

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

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

Sie können bei Bedarf weitere Schnickschnack wie Farben, Präfixe, Zeilennummern usw. hinzufügen.

Stéphane Chazelas
quelle
Rufen Sie Ihre dieFunktion in der Praxis jemals mit mehreren Argumenten auf? (Wenn ja, können Sie ein Beispiel nennen?) Ich verwende eine fast identische dieFunktion, verwende aber "$*"stattdessen, welche könnte mehr sein, als Sie beabsichtigen?
jrw32982 unterstützt Monica
3
Der Wert von "$@"ist, dass es mehrzeilige Nachrichten ermöglicht, ohne dass wörtliche Zeilenumbrüche hinzugefügt werden müssen.
Charles Duffy
1
@ jrw32982 Wenn Sie "$*"zum Verknüpfen von Argumenten mit Leerzeichen verwenden, müssen Sie auch $IFSSPC einstellen , damit dies in allen Kontexten funktioniert, einschließlich der Kontexte, in denen $IFSÄnderungen vorgenommen wurden. Alternativ mit ksh/ zshkönnen Sie verwenden , print -r -- "$@"oder echo -E - "$@"in zsh.
Stéphane Chazelas
@CharlesDuffy Ja, aber ich habe das noch nie im Zusammenhang mit einer die-Typ-Funktion gesehen. Was ich frage, ist: Haben Sie in der Praxis jemals jemanden schreiben sehen, um die "unable to blah:" "some error"eine zweizeilige Fehlermeldung zu erhalten?
jrw32982 unterstützt Monica
@ StéphaneChazelas Guter Punkt. Also (in meiner Formulierung) sollte es sein die() { IFS=" "; printf >&2 "%s\n" "$*"; exit 1; }. Haben Sie diese dieFunktion schon einmal persönlich verwendet , printfum eine mehrzeilige Fehlermeldung zu generieren, indem Sie mehrere Argumente übergeben? Oder übergeben Sie immer nur ein einziges Argument, diesodass der Ausgabe nur eine einzige neue Zeile hinzugefügt wird?
jrw32982 unterstützt Monica
-1

Ich habe das oft als Test für eine leere Zeichenkette gesehen:

if [ "x$foo" = "x" ]; then ...
wef
quelle
Sollte "=" - behoben worden sein.
WEF
3
Diese Praxis stammt buchstäblich aus den 1970er Jahren. Es gibt keinen Grund auch immer es mit jeder Schale zu verwenden , die mit dem 1992 POSIX sh Standard (solange richtig kompatibel ist unter Angabe verwendet wird , und jetzt-obsolescent Funktionalität wie -a, -o, (und )wie derectives zu sagen , testmehrere Operationen in einem einzigen Aufruf zu kombinieren werden vermieden (siehe OB-Marker in pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html ).
Charles Duffy
Nicht-Linux-Unices, die bis in die 90er Jahre mit dem originalen 'sh' ausgeliefert wurden - vielleicht noch. In meiner damaligen Arbeit musste ich tragbare Installationsskripte schreiben und habe dieses Konstrukt verwendet. Ich habe mir gerade die Installationsskripte angesehen, die NVidia für Linux bereitstellt, und sie verwenden immer noch dieses Konstrukt.
Wef
NVidia kann es verwenden, aber das heißt nicht, dass es dafür eine technische Begründung gibt. Die Entwicklung des Frachtkultes im kommerziellen UNIX ist leider weit verbreitet. Sogar Heirloom Bourne hat diesen Fehler nicht in Frage gestellt - die SunOS-Codebasis (die als letzte kommerzielle UNIX-Version eine Nicht-POSIX- /bin/shVersion enthielt und die unmittelbare Vorgängerin von Heirloom Bourne) hatte ihn ebenfalls nicht.
Charles Duffy
1
Ich trage keinen Hut, daher kann ich nicht versprechen, ein YouTube-Video zu veröffentlichen, wenn jemand eine Shell aufdeckt, die mit diesem Bug nach 1990 auf einem kommerziellen UNIX veröffentlicht wurde. Aber wenn ich das täte, wäre es verlockend . :)
Charles Duffy