Это неправильный способ написания:
#!/bin/sh -
set -e # Exit if any command fails
# If multiple args given run this script once for each arg
if [ "$#" -gt 1 ]; then
for arg do
"$0" "$arg"
done
exit
fi
Я думаю, что автор хотел проверить, является ли второй аргумент непустой строкой (что не то же самое, что проверить, есть ли более 1 аргумента, так как второй аргумент может быть передан, но быть пустой строкой).
test somestring
Краткая форма
test -n somestring
Возвращает true, если somestring
не является пустой строкой. (test ''
возвращает false, test anythingelse
возвращает true (но остерегайтесь test -t
в некоторых оболочках, который вместо этого проверяет, является ли stdout терминалом)).
Однако автор забыл кавычки вокруг переменной (на самом деле, на всех переменных). Это означает, что содержимое $2
подчиняется оператору split+glob. Поэтому если $2
содержит символы $IFS
(пробел, табуляция и новая строка по умолчанию) или glob-символы (*
, ?
, [...]
), это не будет работать должным образом.
И если $2
пуст (например, когда передано менее 2 аргументов или второй аргумент пуст), test $2
становится test
, а не test ''
. test
вообще не получает никакого аргумента (пустого или иного).
К счастью, в этом случае test
без аргументов возвращает false. Это немного лучше, чем test -n $2
, который вместо этого вернул бы true (так как он стал бы test -n
, таким же, как test -n -n
), так что этот код, похоже, работает в некоторых случаях.
Подведем итоги:
для проверки передачи 2 и более аргументов:
[ "$#" -gt 1 ]
или
[ "$#" -ge 2 ]
чтобы проверить, является ли переменная непустой:
[ -n "$var" ]
[ "$var" != '' ]
[ "$var" ]
все они надежны в POSIX-реализациях [
, но если вам приходится иметь дело с очень старыми системами, возможно, придется использовать
[ '' != "$var" ]
вместо этого для реализаций [
, которые подавляются значениями $var
, например =
, -t
, (
)...
для проверки, определена ли переменная (это можно использовать для проверки, передается ли скрипту второй аргумент, но использование $#
намного проще для чтения и более идиоматично):
[ "${var+defined}" = defined]
(или эквивалент с помощью формы test
. Использование псевдонима [
для команды test
является более распространенным).
Теперь о разнице между cmd1 && cmd2
и if cmd1; then cmd2; fi
.
Оба запускают cmd2
только в случае успешного выполнения cmd1
. Разница в том, что в случае &&
статус выхода из общего списка команд будет принадлежать последней команде, которая была выполнена (поэтому код failure, если cmd1
не возвращает true (хотя это не мешает set -e
здесь)), а в случае if
, это будет значение cmd2
или 0 (успех), если cmd2
не выполняется.
Поэтому в случаях, когда cmd1
используется в качестве условия (когда его отказ не должен рассматриваться как проблема), обычно лучше использовать if
, особенно если это последнее, что вы делаете в скрипте, так как это определит статус выхода вашего скрипта. Это также делает код более разборчивым.
Форма cmd1 && cmd2
чаще используется как условия, как в:
if cmd1 && cmd2; then...
while cmd1 && cmd2; do...
То есть в контекстах, где нам важен статус выхода обеих команд.
Добавляю ответ здесь, так как я не могу комментировать Хиен Ли без дополнительных очков репутации. Я следовал руководству Хиен Ли, но мне все равно не повезло. Изучив системные сообщения, я обнаружил, что в журнале регистрируется трассировка стека python с зависимостью от gtkspell версии 3. После удаления gtkspell и обновления до библиотеки gtkspell3 это сработало
.