Znaczenie błędu „[: zbyt wiele argumentów” z if [] (nawiasy kwadratowe)

211

Nie mogłem znaleźć żadnego prostego, prostego zasobu, który wyjaśnia znaczenie i naprawę następującego błędu powłoki BASH, więc publikuję to, co znalazłem po zbadaniu.

Błąd:

-bash: [: too many arguments

Google Wersja: bash open square bracket colon too many arguments .

Kontekst: warunek if w pojedynczych nawiasach kwadratowych z prostym operatorem porównania, takim jak równy, większy niż itp., Na przykład:

VARIABLE=$(/some/command);
if [ $VARIABLE == 0 ]; then
  # some action
fi 
user56reinstatemonica8
źródło
1
Gdzie jest kod, który spowodował ten konkretny błąd?
Anderson Green,

Odpowiedzi:

353

Jeśli twój $VARIABLEjest ciągiem zawierającym spacje lub inne znaki specjalne, a używane są pojedyncze nawiasy kwadratowe (co jest skrótem do testpolecenia), to ciąg może być podzielony na wiele słów. Każdy z nich jest traktowany jako osobny argument.

Tak więc jedna zmienna jest podzielona na wiele argumentów :

VARIABLE=$(/some/command);  
# returns "hello world"

if [ $VARIABLE == 0 ]; then
  # fails as if you wrote:
  # if [ hello world == 0 ]
fi 

To samo dotyczy każdego wywołania funkcji, które odkłada ciąg znaków zawierający spacje lub inne znaki specjalne.


Łatwa naprawa

Zawiń dane wyjściowe zmiennej w podwójne cudzysłowy, zmuszając ją do pozostania jako jeden ciąg (a zatem jeden argument). Na przykład,

VARIABLE=$(/some/command);
if [ "$VARIABLE" == 0 ]; then
  # some action
fi 

Proste. Przejdź jednak do „Uważaj też ...” poniżej, jeśli nie możesz zagwarantować, że zmienna nie będzie ciągiem pustym lub ciągiem znaków, który nie zawiera nic oprócz białych znaków.


Lub alternatywną poprawką jest użycie podwójnych nawiasów kwadratowych (co jest skrótem dla new testpolecenia).

Istnieje to jednak tylko w bash (i najwyraźniej korn i zsh), więc może nie być kompatybilny z domyślnymi powłokami wywoływanymi przez /bin/shitp.

Oznacza to, że w niektórych systemach może działać z konsoli, ale nie jest wywoływany gdzie indziej, na przykład zcron , w zależności od konfiguracji wszystkiego.

Wyglądałoby to tak:

VARIABLE=$(/some/command);
if [[ $VARIABLE == 0 ]]; then
  # some action
fi 

Jeśli twoje polecenie zawiera podwójne nawiasy kwadratowe i wyświetlasz błędy w logach, ale działa ono z konsoli, spróbuj zamienić [[alternatywę na sugerowaną tutaj alternatywę lub upewnij się, że cokolwiek uruchamia skrypt używa powłoki obsługującej [[aka new test.


Uważaj również na [: unary operator expectedbłąd

Jeśli widzisz błąd „zbyt wielu argumentów”, możliwe, że otrzymujesz ciąg znaków z funkcji o nieprzewidywalnym wyniku. Jeśli możliwe jest również uzyskanie pustego łańcucha (lub całego łańcucha spacji), byłoby to traktowane jako zero argumentów, nawet przy powyższej „szybkiej poprawce”, i nie powiodło się[: unary operator expected

To samo „gotcha”, jeśli jesteś przyzwyczajony do innych języków - nie spodziewasz się, że zawartość zmiennej zostanie skutecznie wydrukowana w takim kodzie, zanim zostanie sprawdzona.

Oto przykład, który zapobiega zarówno błędom, jak [: too many argumentsi [: unary operator expectedbłędom: zastąpienie wyjścia wartością domyślną, jeśli jest puste (w tym przykładzie, 0), z podwójnymi cudzysłowami owiniętymi wokół całej rzeczy:

VARIABLE=$(/some/command);
if [ "${VARIABLE:-0}" == 0 ]; then
  # some action
fi 

(tutaj akcja nastąpi, jeśli $ VARIABLE ma wartość 0 lub jest pusta. Naturalnie, powinieneś zmienić 0 (wartość domyślna) na inną wartość domyślną, jeśli pożądane jest inne zachowanie)


Uwaga końcowa: Ponieważ [jest to skrót test, wszystkie powyższe dotyczy również błędu test: too many arguments(i również test: unary operator expected)

user56reinstatemonica8
źródło
Jeszcze lepszym sposobem jesti=$(some_command); i=$((i)); if [ "$i" == 0 ] ...
Jo So
1
Wystąpił problem polegający na tym, że Shellscript używający BASH jako interpretera, gdy był wykonywany przez terminal, działał dobrze, ale gdy był wykonywany przez Crontab, miał takie awarie i wysyłał lokalny e-mail przez Postfix, informując o tym błędzie i zorientowałem się, że był IF dla zmiennej, która miała znaki specjalne. Podwójne cytaty uratowały mi życie. Dziękuję Ci :)!
ivanleoncz
13

Po prostu wpadł na to stanowisko, uzyskując ten sam błąd, próbując testu, jeśli dwie zmienne są zarówno pusta (lub nie jest pusty). To okazuje się być złożonym porównaniem - 7.3. Inne operatory porównania - Advanced Bash-Scripting Guide ; i pomyślałem, że powinienem pamiętać, co następuje:

  • Na początku -emyślałem, że to znaczy „pusty”; ale to oznacza, że ​​„plik istnieje” - służy -zdo testowania pustej zmiennej (ciąg)
  • Zmienne łańcuchowe muszą być cytowane
  • W celu porównania logicznego ORAZ albo:
    • użyj dwóch testsi &&ich:[ ... ] && [ ... ]
    • lub użyj -aoperatora w jednym test:[ ... -a ... ]

Oto działające polecenie (przeszukuje wszystkie pliki txt w katalogu i odrzuca te, które grepzawierają oba oba słowa):

find /usr/share/doc -name '*.txt' | while read file; do \
  a1=$(grep -H "description" $file); \
  a2=$(grep -H "changes" $file); \
  [ ! -z "$a1" -a ! -z "$a2"  ] && echo -e "$a1 \n $a2" ; \
done

Edytuj 12 sierpnia 2013: powiązana notatka o problemie:

Zauważ, że podczas sprawdzania równości łańcucha za pomocą klasycznego test(pojedynczy nawias kwadratowy [) MUSISZ mieć spację między operatorem „jest równy”, co w tym przypadku jest pojedynczym =znakiem „równa się” (chociaż dwa znaki równości ==wydają się być akceptowane jako równość operator też). Nie udaje się to (po cichu):

$ if [ "1"=="" ] ; then echo A; else echo B; fi 
A
$ if [ "1"="" ] ; then echo A; else echo B; fi 
A
$ if [ "1"="" ] && [ "1"="1" ] ; then echo A; else echo B; fi 
A
$ if [ "1"=="" ] && [ "1"=="1" ] ; then echo A; else echo B; fi 
A

... ale dodaj przestrzeń - i wszystko wygląda dobrze:

$ if [ "1" = "" ] ; then echo A; else echo B; fi 
B
$ if [ "1" == "" ] ; then echo A; else echo B; fi 
B
$ if [ "1" = "" -a "1" = "1" ] ; then echo A; else echo B; fi 
B
$ if [ "1" == "" -a "1" == "1" ] ; then echo A; else echo B; fi 
B
sdaau
źródło
Czy możesz podać przykład powłoki bash ((A || B) && C)?
jww
patrz pytanie 3826425 i 14964805
splaisan
To naprawdę nie jest tak przydatne w odpowiedzi, ponieważ nie pokazuje, jak całkowicie zawrzeć polecenie w nawiasach kwadratowych, co jest potrzebne na przykład w przypadku pętli.
Timothy Swan
5

Innym scenariuszem, w którym można uzyskać błąd [: too many argumentslub [: a: binary operator expectedbłąd, jest próba przetestowania wszystkich argumentów"$@"

if [ -z "$@" ]
then
    echo "Argument required."
fi

Działa poprawnie, jeśli zadzwonisz foo.shlub foo.sh arg1. Ale jeśli przekażesz wiele argumentów foo.sh arg1 arg2, otrzymasz błędy. Jest tak, ponieważ jest rozwijany do [ -z arg1 arg2 ], co nie jest prawidłową składnią.

Prawidłowy sposób sprawdzenia istnienia argumentów to [ "$#" -eq 0 ]. ( $#to liczba argumentów).

wisbucky
źródło
2

Czasami Jeśli przypadkowo dotkniesz klawiatury i usuniesz spację.

if [ "$myvar" = "something"]; then
    do something
fi

Wywoła ten komunikat o błędzie. Zwróć uwagę, że wymagana jest spacja przed „]”.

Kemin Zhou
źródło
1
Myślę, że skutkuje to innym błędem składni, takim jak: linia 21: [: brakuje `] '
Joe Holloway
1

Miałem ten sam problem ze swoimi skryptami. Ale kiedy dokonałem pewnych modyfikacji, zadziałało to dla mnie. Podobało mi się to: -

export k=$(date "+%k");
if [ $k -ge 16 ] 
    then exit 0; 
else 
    echo "good job for nothing"; 
fi;

w ten sposób rozwiązałem mój problem. Mam nadzieję, że ci to pomoże.

Kidane
źródło