Почему не используется несколько команд с || или && условная работа?

12

Это работает в командной строке (bash, dash):

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

Тем не менее, я пытаюсь написать сценарий оболочки POSIX , он начинается так:

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

И я не знаю почему, но я не понимаю :

Данный аргумент пуст.

если я назову скрипт так:

./test_empty_argument ""

Это почему?

LinuxSecurityFreak
источник
5
См. Как проверить, является ли переменная пустой или содержит только пробелы? способы проверки, если переменная пуста, не установлена ​​или содержит только пробелы. Проблема в этом вопросе не имеет никакого отношения к этому.
ilkkachu
1
Просто используйтеif [ X”” = X”$var” ] ; then echo isempty ; fi
user2497
3
@ user2497 Нет причин использовать это в любой оболочке, выпущенной за последние 20 лет. Это обходной путь для старых, глючных оболочек.
chepner
@chepner Так что это не правильное решение? Что-то еще нужно использовать?
user2497
6
[ "" = "$var" ]будет работать нормально; цитируемая пустая строка не будет удалена из списка аргументов [. Но и в этом нет необходимости, потому что [ -z "$var" ] тоже работает просто отлично.
chepner

Ответы:

37

Обратите внимание, что ваша линия

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

это так же, как

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

( ;в большинстве случаев без кавычек можно заменить символ новой строки)

Это означает, что exit 1оператор всегда выполняется независимо от того, сколько аргументов было передано в сценарий. Это, в свою очередь, означает, что сообщение The given argument is empty.никогда не будет напечатано.

Чтобы выполнить более одного оператора после теста с использованием «синтаксиса короткого замыкания», сгруппируйте операторы в { ...; }. Альтернативой является использование правильного ifвыражения (которое, IMHO, выглядит более чистым в скрипте):

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

У вас та же проблема со вторым тестом.


относительно

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

Это будет работать для данного примера, но общий

some-test && command1 || command2

бы не быть таким же , как

if some-test; then
    command1
else
    command2
fi

Вместо этого это больше похоже на

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

или

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

То есть, если тест или первая команда не пройдены, выполняется вторая команда, что означает, что она потенциально может выполнить все три задействованных оператора.

Кусалананда
источник
18

Эта:

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

не является:

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

Но вместо этого есть:

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

Ваш сценарий завершается независимо от того, сколько аргументов вы ему передали.

Мур
источник
8

Один из способов сделать его более читабельным - это определить dieфункцию (а-ля perl), например:

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

# then:

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

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

Вы можете добавить больше звонков и свистков, таких как цвета, префикс, номер строки ... при необходимости.

Стефан Шазелас
источник
На практике вы когда-нибудь вызывали свою dieфункцию с несколькими аргументами? (Если да, можете ли вы привести пример?) Я использую почти идентичную dieфункцию, но использую "$*"вместо этого, что может быть больше, чем вы собираетесь?
jrw32982 поддерживает Монику
3
Значение в "$@"том, что он позволяет многострочным сообщениям без необходимости добавлять буквенные переводы строк.
Чарльз Даффи
1
@ jrw32982, использование "$*"для объединения аргументов с пробелами также означает, что вам нужно установить $IFSSPC, чтобы он работал во всех контекстах, включая те, где $IFSон был изменен. В качестве альтернативы с ksh/ zsh, вы можете использовать print -r -- "$@"или echo -E - "$@"в zsh.
Стефан
@CharlesDuffy Да, но я никогда не видел, чтобы это делалось в контексте dieфункции -типа. Я спрашиваю: на практике вы когда-нибудь видели, чтобы кто-нибудь писал die "unable to blah:" "some error", чтобы получить сообщение об ошибке в 2 строки?
jrw32982 поддерживает Монику
@ StéphaneChazelas Хороший вопрос. Так (по моей формулировке) так и должно быть die() { IFS=" "; printf >&2 "%s\n" "$*"; exit 1; }. Вы когда-нибудь лично использовали такую dieфункцию, чтобы printfгенерировать многострочное сообщение об ошибке, передавая несколько аргументов? Или вы передаете только один аргумент, чтобы dieон добавлял только один символ новой строки в свой вывод?
jrw32982 поддерживает Монику
-1

Я часто видел это как тест для пустой строки:

if [ "x$foo" = "x" ]; then ...
Визового
источник
Должно было быть "=" - исправлено.
ФВС
3
Эта практика буквально с 1970-х годов. Там нет никаких оснований , чтобы использовать его с любой оболочкой, которая соответствует стандарту ш 1992 POSIX (до тех пор , как правильное цитирование используется и теперь устаревшее функциональность , такие как -a, -o, (и , )как derectives сказать , testчтобы объединить несколько операций в одном вызове избегать; см. маркеры OB в pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html ).
Чарльз Даффи
Un-linux юниты поставлялись с оригинальным 'sh' еще в 90-х - возможно, до сих пор. В то время мне приходилось писать сценарии переносимой установки, и я использовал эту конструкцию. Я только что посмотрел сценарии установки, которые NVidia поставляет для Linux, и они все еще используют эту конструкцию.
ФВС
NVidia может использовать его, но это не значит, что у них есть какое-то техническое обоснование для этого; развитие культа грузов в коммерческой UNIX печально распространено. Даже в Heirloom Bourne такой ошибки нет - поэтому база кода SunOS (последняя коммерческая UNIX, которая выпустила не POSIX /bin/shи непосредственный предшественник Heirloom Bourne) также не имела ее.
Чарльз Даффи
1
Я не ношу шляпу, поэтому я не могу обещать опубликовать видео на YouTube, съев его, если кто-то включит оболочку, опубликованную в коммерческой UNIX с этой ошибкой после 1990 года ... но если бы я это сделал, это было бы заманчиво , :)
Чарльз Даффи