Dlaczego nie używa wielu poleceń z || czy && praca warunkowa?


12

Działa to w wierszu poleceń powłoki (bash, myślnik):

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

Jednak próbuję napisać skrypt powłoki POSIX , zaczyna się tak:

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

I nie wiem dlaczego, ale nie dostaję wiadomości :

Podany argument jest pusty.

jeśli wywołam skrypt w następujący sposób:

./test_empty_argument ""

Dlaczego?


5
Zobacz Jak mogę sprawdzić, czy zmienna jest pusta lub zawiera tylko spacje? dla sposobów testowania, czy zmienna jest pusta, nieuzbrojona lub zawiera tylko spacje. Problem w tym pytaniu nie ma z tym nic wspólnego.
ilkkachu

1
Wystarczy użyćif [ X”” = X”$var” ] ; then echo isempty ; fi
user2497

3
@ user2497 Nie ma powodu, aby używać tego w dowolnej powłoce wydanej w ciągu ostatnich 20 lat. To obejście dla starych, wadliwych pocisków.
chepner,

@chepner Więc to nie jest prawidłowe rozwiązanie? Czy trzeba użyć czegoś jeszcze?
user2497

6
[ "" = "$var" ]działałoby dobrze; cytowany pusty ciąg nie zostanie usunięty z listy argumentów [. Ale to też nie jest konieczne, ponieważ działa [ -z "$var" ] również dobrze.
chepner,

Odpowiedzi:


37

Zauważ, że twoja linia

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

to jest to samo co

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

(cytat ;można w większości przypadków zastąpić znakiem nowej linii)

Oznacza to, że exit 1instrukcja jest zawsze wykonywana bez względu na liczbę argumentów przekazanych do skryptu. To z kolei oznacza, że ​​wiadomość The given argument is empty.nigdy nie miałaby szansy na wydrukowanie.

Aby wykonać więcej niż jedną instrukcję po teście przy użyciu „składni zwarciowej”, zgrupuj instrukcje w { ...; }. Alternatywą jest użycie poprawnej ifinstrukcji (która w skrypcie IMHO wygląda na czystszą):

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

Masz ten sam problem z drugim testem.


Jeżeli chodzi o

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

To działałoby w podanym przykładzie, ale ogólnym

some-test && command1 || command2

by nie być taka sama jak

if some-test; then
    command1
else
    command2
fi

Zamiast tego jest bardziej jak

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

lub

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

Oznacza to, że jeśli test lub pierwsze polecenie zakończy się niepowodzeniem, drugie polecenie zostanie wykonane, co oznacza, że ​​może wykonać wszystkie trzy zaangażowane instrukcje.


18

To:

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

nie jest:

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

Ale zamiast tego jest:

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

Twój skrypt kończy działanie niezależnie od liczby argumentów, które mu przekazałeś.


8

Jednym ze sposobów uczynienia go bardziej czytelnym jest zdefiniowanie diefunkcji (à la perl), takiej jak:

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

# then:

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

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

Możesz dodać więcej dzwonków i gwizdków, takich jak kolory, prefiks, numer linii ... w razie potrzeby.


W praktyce, czy kiedykolwiek wywołujesz swoją diefunkcję z wieloma argumentami? (Jeśli tak, czy możesz podać przykład?) Używam prawie identycznej diefunkcji, ale "$*"zamiast tego używaj , co może być bardziej tym, czego zamierzasz?
jrw32982 obsługuje Monikę

3
Wartość "$@"polega na tym, że pozwala na wieloliniowe wiadomości bez konieczności dodawania dosłownych znaków nowej linii.
Charles Duffy

1
@ jrw32982, użycie "$*"do łączenia argumentów spacjami oznacza również, że musisz ustawić $IFSSPC, aby działał we wszystkich kontekstach, w tym w tych, w których $IFSzostał zmodyfikowany. Alternatywnie za pomocą ksh/ zshmożesz użyć print -r -- "$@"lub echo -E - "$@"w zsh.
Stéphane Chazelas,

@CharlesDuffy Tak, ale nigdy tego nie widziałem w kontekście diefunkcji -type. Pytam: czy w praktyce widziałeś kiedykolwiek kogoś die "unable to blah:" "some error", kto pisze , w celu otrzymania 2-liniowego komunikatu o błędzie?
jrw32982 obsługuje Monikę

@ StéphaneChazelas Dobra uwaga. Tak więc (w moim sformułowaniu) powinno być die() { IFS=" "; printf >&2 "%s\n" "$*"; exit 1; }. Czy kiedykolwiek osobiście używałeś tego rodzaju diefunkcji, aby printfwygenerować wieloliniowy komunikat o błędzie, przekazując wiele argumentów? A może przekazujesz tylko jeden argument, aby diedodawał tylko jeden nowy wiersz do wyniku?
jrw32982 obsługuje Monikę

-1

Często widziałem to jako test na pusty ciąg:

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

Powinny być „=” - naprawione.
wef

3
Ta praktyka pochodzi dosłownie z lat siedemdziesiątych. Nie ma żadnego powodu, aby go używać z dowolnym skorupy, który jest zgodny ze standardem sh 1992 POSIX (tak długo poprawne cytowanie jest używany i teraz-przestarzałe funkcjonalność taką jak -a, -o, (i )jak derectives powiedzieć testłączyć wiele operacji w jednym wywołaniu są unikane; patrz znaczniki OB w pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html ).
Charles Duffy

Jednorożce inne niż linux wysyłane z oryginalnym „sh” dobrze w latach 90-tych - być może nadal. W tym czasie musiałem pisać przenośne skrypty instalacyjne i korzystałem z tej konstrukcji. Właśnie spojrzałem na skrypty instalacyjne, które NVidia dostarcza dla systemu Linux i nadal używają tej konstrukcji.
wef

NVidia może z niego korzystać, ale nie oznacza to, że mają jakieś techniczne uzasadnienie; Niestety kultowy rozwój komercyjnego systemu UNIX jest niestety powszechny. Nawet Heirloom Bourne nie ma omawianego błędu - więc baza kodu SunOS (będąca ostatnim komercyjnym systemem UNIX, który wysłał nie-POSIX /bin/sh, i bezpośredni poprzednik Heirlooma Bourne'a) również go nie miała.
Charles Duffy

1
Nie noszę czapki, więc nie mogę obiecać, że opublikuję film na YouTube jedzący go, gdyby ktoś odkrył powłokę opublikowaną w komercyjnym systemie UNIX z tym błędem po 1990 roku ... ale gdybym to zrobił, byłoby to kuszące . :)
Charles Duffy
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.