Я сделал сценарий Perl, который делает что-то подобное тому, о чем Вы говорите:
В основном это просто пересекает каталог, вычисляя SHA1sum файлов в нем, хешируя его и соединяющих соответствий. Это пригодилось на многих, многих случаях.
Точный язык, используемый в Единственной спецификации UNIX для описания значения set -e
:
Когда эта опция идет, если простая команда перестанет работать по какой-либо из причин, перечисленных в Последствиях Ошибок Shell, или возвратит значение статуса выхода> 0 и не будет [условным выражением или отрицаемой командой], то оболочка должна сразу выйти.
Существует неоднозначность относительно того, что происходит, когда такая команда происходит в подоболочке. С практической точки зрения вся подоболочка может сделать, выйти и возвратить ненулевое состояние родительской оболочке. Выйдет ли родительская оболочка в свою очередь, зависит от того, переводит ли это ненулевое состояние в простую команду, переставшую работать в родительской оболочке.
Один такой проблематичный случай является тем, с которым Вы встретились: ненулевой статус возврата от замены команды. Так как это состояние проигнорировано, оно не заставляет родительскую оболочку выходить. Поскольку Вы уже обнаружили, способ принять статус выхода во внимание состоит в том, чтобы использовать замену команды в простом присвоении: затем статус выхода присвоения является статусом выхода последней замены команды в присвоении (присвоениях).
Обратите внимание, что это будет работать, как предназначено, только если существует единственная замена команды, поскольку только состояние последней замены принято во внимание. Например, следующая команда успешна (и согласно стандарту и в каждой реализации, которую я видел):
a=$(false)$(echo foo)
Другой случай для наблюдения за является явными подоболочками: (somecommand)
. Согласно интерпретации выше, подоболочка может возвратить ненулевое состояние, но так как это не простая команда в родительской оболочке, родительская оболочка должна продолжиться. На самом деле все оболочки, о которых я знаю, действительно делают родительский возврат в этой точке. В то время как это полезно во многих случаях такой как (cd /some/dir && somecommand)
где круглые скобки используются для хранения операции, такой как изменение текущего каталога локальной, это нарушает спецификацию если set -e
выключен в подоболочке, или если бы подоболочка возвращает ненулевое состояние способом, которое не завершило бы ее, такие как использование !
на истинной команде. Например, весь пепел, удар, pdksh, ksh93 и zsh выходит без отображения foo
на следующих примерах:
set -e; (set +e; false); echo "This should be displayed"
set -e; (! true); echo "This should be displayed"
Все же никакая простая команда не перестала работать в то время как set -e
был в действительности!
Третий проблематичный случай является элементами в нетривиальном конвейере. На практике все оболочки игнорируют отказы элементов конвейера кроме последнего и показывают одно из двух поведений относительно последнего конвейерного элемента:
Как прежде, выключая set -e
или использование отрицания в последнем элементе конвейера заставляет это возвращать ненулевое состояние способом, которое не должно завершать оболочку; оболочки кроме ATT ksh и zsh затем выйдут.
Bash pipefail
опция заставляет конвейер сразу выйти под set -e
если какой-либо из его элементов возвращает ненулевое состояние.
Обратите внимание, что как дальнейшая сложность, удар выключает set -e
в подоболочках, если это не находится в режиме POSIX (set -o posix
или имейте POSIXLY_CORRECT
в среде, когда удар запускается).
Все это показывает, что спецификация POSIX, к сожалению, делает плохое задание при определении -e
опция. К счастью, существующие оболочки главным образом последовательны в своем поведении.
Если вы используете Bash 4.4 или более позднюю версию , вы можете использовать параметр shopt
inherit_errexit
, чтобы сделать именно это. Вы можете проверить совместимость из Bash, используя echo $BASH_VERSION
.
Вот шебанг , который вы использовали бы, если бы был установлен Bash 4.4 или более поздней версии и стоял перед /bin
в вашем$PATH
:
#!/usr/bin/env -S bash -euET -o pipefail -O inherit_errexit
-S
предназначен для того, чтобы уговорить Linux env
принять более одного аргумента для bash
, как любезно указал @UVV и далее объяснил на StackOverflow .
inherit_errexit
является опцией shopt
, а остальные аргументы являются опциями set
. В большинстве современных итераций их можно передать непосредственно в bash
при вызове оболочки.
Давайте рассмотрим варианты, которые вы уже использовали:
-u
/-o nounset
, как двусмысленно намекает название, запрещает разыменование переменных, которые не были установлены; например, $IJUSTMADETHISUP
. В основном это помогает защититься от опечаток или недосмотров при копировании -вставки из Stack Overflow -e
/ -o errexit
выполняет некоторые из того, что вы запрашиваете :при прямом вызове†, команды оболочки с ненулевыми кодами состояния вызовут завершение работы оболочки с ошибкой. То есть все, что предшествует строке скрипта оболочки, должно «работать правильно», чтобы эта строка могла быть выполнена. -o pipefail
необходимо, чтобы распространить эту гарантию на команды, вывод которых перенаправляется с помощью конвейера ввода/вывода |
. † т. е. не из подоболочки(...)
Теперь о параметрах, которые я добавил:
-O inherit_errexit
дополнительно расширяет эту функциональность (выхода при ненулевом коде состояния )для команд, вызываемых из подоболочек. Это закрывает важную лазейку, поскольку подоболочки используются для подстановки команд $(...)
,`...`
и подстановки процессов <(...)
, >(...)
,и оба они встречаются во многих сценариях оболочки в дикой природе. ‡ -E
/-o errtrace
и -T
/-o functrace
предназначены для сравнительно редкого случая, когда вы используете trap
для выполнения действия, когда оболочка получает сигнал. Эти две опции расширяют обработчики сигналов до внутренних тел функций оболочки для сигналов ERR
и сигналов DEBUG
/RETURN
соответственно. ‡ Это всего лишь одна из причин, по которой для подстановки команд следует предпочесть синтаксис $(...)
, поскольку круглые скобки указывают на то, что вы входите в подоболочку. Он также бывает вложенным.
(Ответ на мое собственное, потому что я нашел решение), Одно решение состоит в том, чтобы всегда присваивать это промежуточной переменной. Таким образом, код возврата ($?
) установлен.
Так
ABC=`exit 1`
echo $?
Произведет 1
(или вместо этого выход, если set -e
присутствует), однако:
echo `exit 1`
echo $?
Произведет 0
после пустой строки. Код возврата эха (или другая команда, которая работала с выводом обратной галочки) заменит 0 кодов возврата.
Я все еще открыт для решений, которые не требуют промежуточной переменной, но это получает меня часть пути.
Интересный момент!
Я никогда не спотыкался через это, потому что я не друг set -e
(Вместо этого я предпочитаю trap ... ERR
) но уже протестированный, что: trap ... ERR
также не фиксируйте ошибки в $(...)
(или старомодные обратные галочки).
Я думаю, что проблема (как так часто), что здесь подоболочку называют и -e
explicitely означает текущую оболочку.
Только другое решение, которое пришло на ум в данный момент, будет состоять в том, чтобы использовать чтение:
ls -l ghost_under_bed | read name
Это бросает ERR
и с -e
оболочка будет завершена. Только проблема: это работает только на команды с одной строкой вывода (или Вы передаете по каналу через что-то, что присоединяется к строкам).
Поскольку OP, на который указывают в его собственном ответе, присваивая вывод подкоманды к переменной, действительно решает проблему; $?
оставлен невредимым.
Однако один пограничный случай может все еще озадачить Вас с ложными отрицательными сторонами (т.е. управлять сбоями, но ошибка приводит в порядок не пузырь), local
объявление переменной:
local myvar=$(subcommand)
будет всегда возвращаться 0
!
bash(1)
указывает на это:
local [option] [name[=value] ...]
... The return status is 0 unless local is used outside a function,
an invalid name is supplied, or name is a readonly variable.
Вот простой тестовый сценарий:
#!/bin/bash
function test1() {
data1=$(false) # undeclared variable
echo 'data1=$(false):' "$?"
local data2=$(false) # declaring and assigning in one go
echo 'local data2=$(false):' "$?"
local data3
data3=$(false) # assigning a declared variable
echo 'local data3; data3=$(false):' "$?"
}
test1
Вывод:
data1=$(false): 1
local data2=$(false): 0
local data3; data3=$(false): 1
VAR=...
и local VAR=...
действительно сбиваемый с толку меня!
– Jonny
10.04.2018, 17:10
Как говорили другие, локальный
всегда возвращает 0. Решение заключается в том, чтобы сначала объявить переменную:
function testcase()
{
local MYRESULT
MYRESULT=$(false)
if (( $? != 0 )); then
echo "False returned false!"
return 1
fi
return 0
}
-вывести:
$ testcase
False returned false!
$
Для выхода при ошибке подстановки команды вы можете явно установить -e
в подоболочке, например:
set -e
x=$(set -e; false; true)
echo "this will never be shown"
set -e; (cd /nonexisting)
. – dubiousjim 13.01.2013, 23:39