Значение ошибки «[: слишком много аргументов» от if [] (квадратные скобки)

212

Я не смог найти ни одного простого и понятного ресурса с описанием значения и исправлением следующей ошибки оболочки BASH, поэтому я публикую то, что нашел после исследования.

Ошибка:

-bash: [: too many arguments

Google-Версия: bash open square bracket colon too many arguments .

Контекст: условие if в квадратных скобках с простым оператором сравнения, например, равно, больше чем, например, например:

VARIABLE=$(/some/command);
if [ $VARIABLE == 0 ]; then
  # some action
fi 
user56reinstatemonica8
источник
1
Где код, вызвавший эту конкретную ошибку?
Андерсон Грин

Ответы:

354

Если ваша $VARIABLEстрока содержит пробелы или другие специальные символы и используются одинарные квадратные скобки (что является сокращением для testкоманды), то строка может быть разбита на несколько слов. Каждый из них рассматривается как отдельный аргумент.

Так что одна переменная разбивается на множество аргументов :

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

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

То же самое будет верно для любого вызова функции, который записывает строку, содержащую пробелы или другие специальные символы.


Легко исправить

Оберните вывод переменной в двойные кавычки, заставив его остаться как одна строка (следовательно, один аргумент). Например,

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

Просто как тот. Но перейдите к пункту «Также будьте осторожны ...» ниже, если вы также не можете гарантировать, что ваша переменная не будет пустой строкой или строкой, которая содержит только пробелы.


Или альтернативным решением является использование двойных квадратных скобок (что является сокращением для new testкоманды).

Это существует только в bash (и, по-видимому, korn и zsh), и поэтому может быть несовместимо с оболочками по умолчанию, вызываемыми и /bin/shт. Д.

Это означает, что в некоторых системах он может работать с консоли, но не при вызове в другом месте, напримерcron , в зависимости от того, как все настроено.

Это будет выглядеть так:

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

Если ваша команда содержит двойные квадратные скобки, подобные этой, и вы получаете ошибки в журналах, но она работает из консоли, попробуйте заменить [[альтернативу, предложенную здесь, или убедитесь, что во всех ваших скриптах используется оболочка, которая поддерживает [[aka new test.


Также остерегайтесь [: unary operator expectedошибок

Если вы видите ошибку «слишком много аргументов», скорее всего, вы получаете строку из функции с непредсказуемым выводом. Если также возможно получить пустую строку (или всю строку пробела), это будет рассматриваться как нулевые аргументы даже с указанным выше «быстрым исправлением» и завершится ошибкой с[: unary operator expected

Это то же самое «гоча», если вы привыкли к другим языкам - вы не ожидаете, что содержимое переменной будет эффективно напечатано в коде, подобном этому, до его оценки.

Вот пример, который предотвращает как ошибки, так [: too many argumentsи [: unary operator expectedошибки: замена вывода значением по умолчанию, если оно пустое (в этом примере, 0), с двойными кавычками, обернутыми вокруг всего:

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

(здесь действие произойдет, если $ VARIABLE равно 0 или пусто. Естественно, вы должны изменить 0 (значение по умолчанию) на другое значение по умолчанию, если требуется другое поведение)


Последнее замечание: поскольку [это сокращение test, все вышесказанное также верно для ошибки test: too many arguments(и также test: unary operator expected)

user56reinstatemonica8
источник
Еще лучший способi=$(some_command); i=$((i)); if [ "$i" == 0 ] ...
Джо Так
1
Я столкнулся с проблемой, когда шеллскрипт, использующий BASH в качестве интерпретатора, когда выполнялся через терминал, работал нормально, но при выполнении через Crontab возникал сбой, подобный этому, и отправлял локальную электронную почту через Postfix, сообщая об этой ошибке, и я понял, что IF для переменной, содержащей специальные символы. Двойные кавычки спасли мне жизнь. Спасибо :)!
ivanleoncz
13

Просто наткнулся на этот пост, получая ту же ошибку, пытаясь проверить, являются ли две переменные обе пустыми (или непустыми). Это оказывается сложным сравнением - 7.3. Другие операторы сравнения - Расширенное руководство по написанию сценариев ; и я подумал, что должен отметить следующее:

  • -eСначала я думал, что это означает «пустой»; но это означает, что «файл существует» - используйте -zдля проверки пустой переменной (строки)
  • Строковые переменные должны быть в кавычках
  • Для составного логического сравнения И:
    • использовать два testс и &&их:[ ... ] && [ ... ]
    • или используйте -aоператор в одном test:[ ... -a ... ]

Вот рабочая команда (поиск по всем txt-файлам в каталоге и выгрузка grepнайденных файлов, содержащих оба слова):

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

Изменить 12 августа 2013: примечание к соответствующей проблеме:

Обратите внимание, что при проверке равенства строк с помощью классического test(одна квадратная скобка [), вы ДОЛЖНЫ иметь пробел между оператором «равно», который в этом случае является единственным =знаком «равно» (хотя два знака ==равенства кажутся принятыми как равенство оператор тоже). Таким образом, это терпит неудачу (молча):

$ 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

... но добавьте пробел - и все выглядит хорошо:

$ 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
источник
Не могли бы вы привести пример оболочки bash ((A || B) && C)?
jww
см. вопросы 3826425 и 14964805
splaisan
Это действительно не очень полезный ответ, потому что он не показывает, как полностью поместить команду в квадратные скобки, что необходимо, например, при наличии цикла.
Тимоти Свон
5

Другой сценарий, который вы можете получить, [: too many argumentsили [: a: binary operator expectedошибки, если вы пытаетесь проверить все аргументы"$@"

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

Это работает правильно, если вы звоните foo.shили foo.sh arg1. Но если вы передадите несколько аргументов, как foo.sh arg1 arg2, вы получите ошибки. Это потому, что он расширяется до [ -z arg1 arg2 ], что не является допустимым синтаксисом.

Правильный способ проверить наличие аргументов есть [ "$#" -eq 0 ]. ( $#количество аргументов).

wisbucky
источник
2

Несколько раз, если вы случайно коснулись клавиатуры и убрали пробел.

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

Вызовет это сообщение об ошибке. Обратите внимание на пробел перед ']'.

Кемин Чжоу
источник
1
Я думаю, что это приводит к другой синтаксической ошибке, такой как: строка 21: [: отсутствующий `] '
Джо Холлоуэй
1

У меня была такая же проблема с моими сценариями. Но когда я сделал некоторые изменения, это сработало для меня. Я сделал это так:

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

таким образом я решил свою проблему. Надеюсь, что это поможет и вам.

Кидане
источник