Обычно это указывает на то, что ваша программа тратит значительное количество времени на ожидание обработки своего вывода. Ожидание ввода-вывода считается отдельно от использования ЦП. Перенаправляя его вывод на /dev/null
, вы устраняете это, и ваша программа тратит больше времени на работу.
Позвольте мне сначала объяснить, почему это происходит. Язык команд оболочки POSIX спец говорит:
The exit status of the if command shall be the exit status of the then or else compound-list that was executed, or zero, if none was executed.
Так как в вашем случае then
часть не выполняется и нет else
статус выхода равен 0. Он также будет равен 0, если вы запустите этот скрипт, используя Баш как в man bash
говорит:
if list; then list; [ elif list; then list; ]... [ else list; ] fi
The if list is executed. If its exit status is zero,
the then list is executed. Otherwise, each elif list is
executed in turn, and if its exit status is zero, the
corresponding then list is executed and the command
completes. Otherwise, the else list is executed, if
present. The exit status is the exit sta‐ tus of the
last command executed, or zero if no condition tested
true.
How can I detect this kind of error in a script if I can't rely on the exit code?
Я могу придумать два способа:
если вы можете изменить свой сценарий, добавьте часть else
в конструкцию if:
#!/bin/sh
foo=1
if [[ ! -z $foo ]]; then
echo abc
else
echo not true
exit 1
fi
если вы получили if от кого-то и не хотите его модифицировать, используйте статический анализатор shellcheck в режиме sh
для поиска возможных ошибок в код и сообщить о них автору:
$ shellcheck -s sh dash-exit-status.sh
In dash-exit-status.sh line 4:
if [[ ! -z $foo ]]; then
^-------------^ SC2039: In POSIX sh, [[ ]] is undefined.
^-- SC2236: Use -n instead of ! -z.
For more information:
https://www.shellcheck.net/wiki/SC2039 -- In POSIX sh, [[ ]] is undefined.
https://www.shellcheck.net/wiki/SC2236 -- Use -n instead of ! -z.
По сути, для меня это ошибка , так как не следует использовать не -POSIX функции в сценариях, которые должны выполняться /bin/sh
который может, но не обязательно должен быть символической ссылкой на Bash.
Обратите внимание, что, как прокомментировал @muru, это не синтаксическая ошибка в том, что касается Dash. [
не является специальным символом оболочки, в отличие от ;
и (
. [
— это обычная команда вроде echo
, только с забавным названием. В Bash/Ksh/Zsh [[
является ключевым словом, подобным if
, и ключевые слова являются частью синтаксиса оболочки, но, что особенно важно, в оболочках, которые не поддерживают , [[
будет обрабатываться как еще одна команда.
Поскольку Dash не поддерживает [[
, он ищется в PATH
как любая другая команда, и поиск завершается ошибкой. Как говорится в ответе Аркадиуша Драбчика , результатом здесь является нулевой статус выхода. Если бы это была настоящая синтаксическая ошибка, оболочка немедленно завершила бы работу с ненулевым статусом -.
How can I detect this kind of error in a script if I can't rely on the exit code?
Ну, технически можно...
#!/bin/dash
[[ $1 == "foo" ]]
case $? in
0) echo "the first argument was 'foo'";;
1) echo "the first argument was something else";;
127) echo "[[ not supported";;
*) echo "there was some other error";;
esac
Или то же самое с ret=$?
и цепочкой из if-elif
с условиями против $ret
. Это несколько неудобно делать, и не лучшее решение здесь,но если вам действительно нужно различать разные коды ошибок какой-то команды, это то, что вам нужно сделать.
Если вы стремитесь обнаружить, в частности, дело Dash против Bash, вы можете добавить дополнительный тест в начале скрипта, чтобы увидеть, правильно ли работает [[
:
#!/bin/bash
if ! [[ a == a ]] 2>/dev/null; then
echo "[[ not supported, exiting." >&2
exit 1
fi
#...
Точно так же вы, вероятно, могли бы добавить тесты для всех других не--POSIX-функций, которые вы используете, но их тестирование всех , конечно, может стать немного громоздким.
Чтобы требовать именно Bash, а не любую оболочку с такими же функциями (Ksh и Zsh совместимы с некоторыми вещами, хотя и не со всеми ), проверьте $BASH_VERSION
в начале скрипта:
#!/bin/bash
if [ -z "$BASH_VERSION" ]; then
echo "Running only with Bash supported, exiting" >&2
exit 1
fi
#...
(Переменная, конечно, может быть установлена вручную, но чем лучше пользователь будет знать, что он делает.)