Собственный вес команды удалит от текущей позиции курсора до начала следующего словесного символа. d$ команды (примечание, это - знак доллара, не 'S') удалит из текущей позиции курсора в конец текущей строки. D является синонимом за d$.
Если Вы используете bash
, можно использовать PIPESTATUS
переменная типа массив для получения статуса выхода каждого элемента конвейера.
$ false | true
$ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
1 0
Если Вы используете zsh
, они выстраивают, назван pipestatus
(вопросы случая!) и индексы массива запускаются в одном:
$ false | true
$ echo "${pipestatus[1]} ${pipestatus[2]}"
1 0
Для объединения их в функции способом, который не теряет значения:
$ false | true
$ retval_bash="${PIPESTATUS[0]}" retval_zsh="${pipestatus[1]}" retval_final=$?
$ echo $retval_bash $retval_zsh $retval_final
1 0
Выполненное вышеупомянутое в bash
или zsh
и Вы получите те же результаты; только один из retval_bash
и retval_zsh
будет установлен. Другой будет пробел. Это позволило бы функции заканчиваться return $retval_bash $retval_zsh
(отметьте отсутствие кавычек!).
Править: Этот ответ является неправильным, но интересным, таким образом, я оставлю его для дальнейшего использования.
!
к команде инвертирует код возврата. http://tldp.org/LDP/abs/html/exit-status.html
# =========================================================== #
# Preceding a _pipe_ with ! inverts the exit status returned.
ls | bogus_command # bash: bogus_command: command not found
echo $? # 127
! ls | bogus_command # bash: bogus_command: command not found
echo $? # 0
# Note that the ! does not change the execution of the pipe.
# Only the exit status changes.
# =========================================================== #
ls
, не инвертируют код выхода bogus_command
– Michael Mrozek♦
03.06.2011, 01:13
С небольшим количеством предосторожности это должно работать:
foo-status=$(mktemp -t)
(foo; echo $? >$foo-status) | bar
foo_status=$(cat $foo-status)
Это портативно, т.е. работает с любым POSIX совместимая оболочка, не требует, чтобы текущий каталог был перезаписываем, и позволяет несколько сценариев с помощью того же приема для выполнения одновременно.
(foo;echo $?>/tmp/_$$)|(bar;exit $(cat /tmp/_$$;rm /tmp/_$$))
Править: вот более сильная версия после комментариев Gilles:
(s=/tmp/.$$_$RANDOM;((foo;echo $?>$s)|(bar)); exit $(cat $s;rm $s))
Edit2: и вот немного более легкий вариант после комментария dubiousjim:
(s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s))
(s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s))
. @Johan: Я соглашаюсь, что легче с Bash, но в некоторых контекстах, зная, как избежать, чтобы Bash стоил того.
– dubiousjim
30.08.2012, 01:25
Запуск с конвейера:
foo | bar | baz
Вот общее решение с помощью только оболочку POSIX и никакие временные файлы:
exec 4>&1
error_statuses="`((foo || echo "0:$?" >&3) |
(bar || echo "1:$?" >&3) |
(baz || echo "2:$?" >&3)) 3>&1 >&4`"
exec 4>&-
$error_statuses
содержит коды состояния любых неудавшихся процессов, в произвольном порядке, с индексами для сообщения, какая команда испустила каждое состояние.
# if "bar" failed, output its status:
echo "$error_statuses" | grep '1:' | cut -d: -f2
# test if all commands succeeded:
test -z "$error_statuses"
# test if the last command succeeded:
! echo "$error_statuses" | grep '2:' >/dev/null
Отметьте кавычки вокруг $error_statuses
в моих тестах; без них grep
не может дифференцироваться, потому что новые строки принуждены к пробелам.
Что я делаю, если это возможно, должен подать код выхода от foo
в bar
. Например, если я знаю это foo
никогда не продолжает линию только с цифрами, затем я могу просто лавировать на коде выхода:
{ foo; echo "$?"; } | awk '!/[^0-9]/ {exit($0)} {…}'
Или если я знаю что вывод от foo
никогда не содержит строку только с .
:
{ foo; echo .; echo "$?"; } | awk '/^\.$/ {getline; exit($0)} {…}'
Это может всегда делаться, если существует некоторый способ добраться bar
работать над всеми кроме последней строки и передавать последнюю строку как ее код выхода.
Если bar
сложный конвейер, чей производит Вас, не нуждаются, можно обойти часть его путем печати кода выхода на другом дескрипторе файла.
exit_codes=$({ { foo; echo foo:"$?" >&3; } |
{ bar >/dev/null; echo bar:"$?" >&3; }
} 3>&1)
После этого $exit_codes
обычно foo:X bar:Y
, но это могло быть bar:Y foo:X
если bar
выходы прежде, чем считать весь его вход или если Вы неудачны. Я думаю, что записи к каналам до 512 байтов являются атомарными на всех нельдах, таким образом, foo:$?
и bar:$?
части не будут смешаны, пока строки тега находятся под 507 байтами.
Если необходимо получить вывод от bar
, это становится трудным. Можно объединить методы выше путем принятия мер к выводу bar
чтобы никогда содержать строку, которая похожа на признак кода выхода но это действительно становится трудным.
output=$(echo;
{ { foo; echo foo:"$?" >&3; } |
{ bar | sed 's/^/^/'; echo bar:"$?" >&3; }
} 3>&1)
nl='
'
foo_exit_code=${output#*${nl}foo:}; foo_exit_code=${foo_exit_code%%$nl*}
bar_exit_code=${output#*${nl}bar:}; bar_exit_code=${bar_exit_code%%$nl*}
output=$(printf %s "$output" | sed -n 's/^\^//p')
И, конечно, существует простая опция использования временного файла для хранения состояния. Простой, но не настолько простой в производстве:
/tmp
единственное место, где сценарий, несомненно, сможет записать файлы. Использовать mktemp
, который не является POSIX, но доступный на всех серьезных нельдах в наше время.foo_ret_file=$(mktemp -t)
{ foo; echo "$?" >"$foo_ret_file"; } | bar
bar_ret=$?
foo_ret=$(cat "$foo_ret_file"; rm -f "$foo_ret_file")
В то время как не точно, что Вы спросили, Вы могли использовать
#!/bin/bash -o pipefail
так, чтобы Ваши каналы возвратили последнее не нулевой возврат.
мог бы немного меньше кодировать
Править: Пример
[root@localhost ~]# false | true
[root@localhost ~]# echo $?
0
[root@localhost ~]# set -o pipefail
[root@localhost ~]# false | true
[root@localhost ~]# echo $?
1
set -o pipefail
в сценарии должно быть более устойчивым, например, в случае, если кто-то выполняет сценарий через bash foo.sh
.
– maxschlepzig
03.06.2011, 12:17
#!/bin/bash -o pipefail
. Ошибка: /bin/bash: line 0: /bin/bash: /tmp/ff: invalid option name
– Felipe Alvarez
24.03.2014, 08:01
#!
строки вне первой, и таким образом, это становится /bin/bash
-o pipefail
/tmp/ff
, вместо необходимого /bin/bash
-o
pipefail
/tmp/ff
-- getopt
(или подобный) анализирующий использование optarg
, который является следующим объектом в ARGV
, как аргумент -o
, таким образом, это перестало работать. Если необходимо было сделать обертку (скажите, bash-pf
это просто сделало exec /bin/bash -o pipefail "$@"
, и поставленный это #!
строка, которая работала бы.См. также: достаточно ярмарка А-ч en.wikipedia.org/wiki/Shebang_%28Unix%29
– lindes
05.12.2015, 02:09
Следующее, 'если' блок будет работать только если 'команда', за которой следуют:
if command; then
# ...
fi
Специфически говорящий, можно выполнить что-то вроде этого:
haconf_out=/path/to/some/temporary/file
if haconf -makerw > "$haconf_out" 2>&1; then
grep -iq "Cluster already writable" "$haconf_out"
# ...
fi
Который будет работать haconf -makerw
и сохраните его stdout и stderr к "$haconf_out". Если возвращенное значение от haconf
верно, затем, 'если' блок будет выполняться и grep
считает "$haconf_out", пытаясь соответствовать ему против "Кластера, уже перезаписываемого".
Заметьте, что каналы автоматически моются; с перенаправлением необходимо будет быть осторожными для удаления "$haconf_out" при выполнении.
Не столь изящный как pipefail
, но законная альтернатива, если эта функциональность не в пределах досягаемости.
Существует 3 распространенных способа сделать это:
Первый путь состоит в том, чтобы установить pipefail
опция (ksh
, zsh
или bash
). Это является самым простым и что это делает в основном установлен статус выхода $?
к коду выхода последней программы, которая выйдет ненулевой (или нуль, если все вышли успешно).
$ false | true; echo $?
0
$ set -o pipefail
$ false | true; echo $?
1
Bash также назвали переменную типа массив $PIPESTATUS
($pipestatus
в zsh
) который содержит статус выхода всех программ в последнем конвейере.
$ true | true; echo "${PIPESTATUS[@]}"
0 0
$ false | true; echo "${PIPESTATUS[@]}"
1 0
$ false | true; echo "${PIPESTATUS[0]}"
1
$ true | false; echo "${PIPESTATUS[@]}"
0 1
Можно использовать 3-й пример команды для получения определенного значения в конвейере, в котором Вы нуждаетесь.
Это является самым громоздким из решений. Выполните каждую команду отдельно и получите состояние
$ OUTPUT="$(echo foo)"
$ STATUS_ECHO="$?"
$ printf '%s' "$OUTPUT" | grep -iq "bar"
$ STATUS_GREP="$?"
$ echo "$STATUS_ECHO $STATUS_GREP"
0 1
ksh
, но от краткого взгляда на он - страница справочника, это не поддерживает $PIPESTATUS
или что-либо подобное. Это действительно поддерживает pipefail
опция все же.
– Patrick
21.04.2013, 18:30
LOG=$(failed_command | successful_command)
– vmrob
08.02.2014, 11:20
Это решение работает, не используя удар определенные функции или временные файлы. Премия: в конце статус выхода является на самом деле статусом выхода и не некоторой строкой в файле.
Ситуация:
someprog | filter
Вы хотите статус выхода от someprog
и вывод от filter
.
Вот мое решение:
((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
результатом этой конструкции является stdout от filter
как stdout конструкции и статуса выхода от someprog
как статус выхода конструкции.
эта конструкция также работает с простой группировкой команды {...}
вместо подоболочек (...)
. подоболочки имеют некоторые последствия среди других стоимость производительности, в которой мы не нуждаемся здесь. прочитайте прекрасное руководство удара для получения дополнительной информации: https://www.gnu.org/software/bash/manual/html_node/Command-Grouping.html
{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | { read xs; exit $xs; } } 4>&1
К сожалению, грамматика удара требует пробелов и точек с запятой для фигурных скобок так, чтобы конструкция стала намного более просторной.
Для остальной части этого текста я буду использовать вариант подоболочки.
Пример someprog
и filter
:
someprog() {
echo "line1"
echo "line2"
echo "line3"
return 42
}
filter() {
while read line; do
echo "filtered $line"
done
}
((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
echo $?
Пример произвел:
filtered line1
filtered line2
filtered line3
42
Примечание: дочерний процесс наследовал открытые дескрипторы файлов от родителя. Это означает someprog
наследует открытый дескриптор файла 3 и 4. Если someprog
записи к дескриптору файла 3 затем это станет статусом выхода. Реальный статус выхода будет проигнорирован потому что read
только чтения однажды.
Если Вы волнуетесь что Ваш someprog
мог бы записать в дескриптор файла 3, или 4 затем лучше закрывать дескрипторы файлов перед вызовом someprog
.
(((((exec 3>&- 4>&-; someprog); echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
exec 3>&- 4>&-
прежде someprog
закрывает дескриптор файла перед выполнением someprog
таким образом для someprog
те дескрипторы файлов просто не существуют.
Это может также быть записано как это: someprog 3>&- 4>&-
Пошаговое объяснение конструкции:
( ( ( ( someprog; #part6
echo $? >&3 #part5
) | filter >&4 #part4
) 3>&1 #part3
) | (read xs; exit $xs) #part2
) 4>&1 #part1
От вверх дном:
#part3
) и право (#part2
) выполняются. exit $xs
также последняя команда канала, и это означает, что строка от stdin будет статусом выхода всей конструкции.#part2
и в свою очередь будет статус выхода всей конструкции.#part5
и #part6
) и право (filter >&4
) выполняются. Вывод filter
перенаправляется к дескриптору файла 4. В #part1
дескриптор файла 4 был перенаправлен к stdout. Это означает что вывод filter
stdout всей конструкции.#part6
печатается к дескриптору файла 3. В #part3
дескриптор файла 3 был перенаправлен к #part2
. Это означает что статус выхода от #part6
будет заключительный статус выхода для всей конструкции.someprog
выполняется. Статус выхода принят #part5
. stdout взят каналом в #part4
и переданный filter
. Вывод от filter
в свою очередь достигнет stdout, как объяснено в #part4
решение lesmana выше может также быть сделано без издержек запуска вложенных подпроцессов при помощи { .. }
вместо этого (помнящий, что эта форма сгруппированных команд всегда должна заканчиваться с точками с запятой). Что-то вроде этого:
{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | stdintoexitstatus; } 4>&1
Я проверил эту конструкцию с версией 0.5.5 тире и колочу версии 3.2.25 и 4.2.42, поэтому даже если некоторые оболочки не поддерживают { .. }
группировка, это - все еще совместимый POSIX.
set -o pipefail
в ksh или любом количестве опрыснутых wait
команды в также. Я думаю, что это может, частично по крайней мере, быть проблема парсинга для ksh, как будто я придерживаюсь использования подоболочек затем, это хорошо работает, но даже с if
чтобы выбрать вариант подоболочки для ksh, но оставить составные команды для других, это перестало работать.
– Greg A. Woods
03.08.2017, 03:42
Итак, я хотел дать ответ наподобие ответа lesmana, но я думаю, что мой, возможно, немного проще и выгоднее чисто-борновского решения:
# You want to pipe command1 through command2:
exec 4>&1
exitstatus=`{ { command1; printf $? 1>&3; } | command2 1>&4; } 3>&1`
# $exitstatus now has command1's exit status.
Я думаю, что это лучше всего объясняется изнутри - command1 выполнит и распечатает свой обычный вывод на stdout (дескриптор файла 1), затем, как только это будет сделано, printf выполнит и напечатает код выхода command1 на своем stdout, но этот stdout перенаправляется на файловый дескриптор 3.
Пока команда command1 запущена, ее стандартный вывод передается по конвейеру в command2 (вывод printf никогда не попадает в command2, потому что мы отправляем его в файловый дескриптор 3 вместо 1, который читает канал).Затем мы перенаправляем вывод command2 в файловый дескриптор 4, чтобы он также оставался вне файлового дескриптора 1 - потому что мы хотим, чтобы файловый дескриптор 1 был свободен немного позже, потому что мы вернем вывод printf в файловый дескриптор 3 обратно в файловый дескриптор. 1 - потому что это то, что будет записано при подстановке команд (обратные кавычки), и это то, что будет помещено в переменную.
Последний кусочек магии состоит в том, что первый exec 4> & 1
мы выполнили как отдельную команду - он открывает файловый дескриптор 4 как копию stdout внешней оболочки. Подстановка команд захватит все, что написано в стандарте с точки зрения команд внутри него, но, поскольку вывод command2 идет в файловый дескриптор 4, что касается подстановки команд, подстановка команды не захватывает это, однако, как только он выходит из подстановки команды, он по-прежнему переходит к общему файловому дескриптору сценария 1.
( exec 4> & 1
должен быть отдельной командой, потому что многие общие оболочки не Мне не нравится, когда вы пытаетесь записать в файловый дескриптор внутри подстановки команды, которая открывается во "внешней" команде, использующей подстановку. Так что это самый простой переносимый способ сделать это.)
Вы можете посмотреть на это менее технически и более игриво, как будто выходные данные команд перепрыгивают друг друга: command1 переходит к command2, тогда вывод printf перескакивает через команду 2, так что command2 не поймает это, а затем вывод команды 2 перескакивает из подстановки команды так же, как printf приземляется как раз вовремя, чтобы быть захваченным подстановкой, так что он заканчивается в переменной, а вывод command2 продолжает свой веселый путь, записанный в стандарт вывод, как в обычном пайпе.
Кроме того, насколько я понимаю, $?
по-прежнему будет содержать код возврата второй команды в конвейере, потому что присвоения переменных, подстановки команд и составные команды фактически прозрачны для кода возврата. команды внутри них, поэтому статус возврата command2 должен распространяться - это, а не необходимость определять дополнительную функцию, поэтому я думаю, что это может быть несколько лучшее решение, чем то, которое предлагает lesmana.
Согласно предостережениям, которые упоминает лесмана, возможно, что command1 в какой-то момент закончится использованием файловых дескрипторов 3 или 4, поэтому для большей надежности вы должны:
exec 4>&1
exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1`
exec 4>&-
Обратите внимание, что в моем примере я использую составные команды, но подоболочки (использование ()
вместо {}
также будет работать, хотя, возможно, будет менее эффективным.)
Команды наследуют файловые дескрипторы от процесса, который их запускает, поэтому все вторая строка унаследует дескриптор файла четыре, а составная команда, за которой следует 3> & 1
, унаследует дескриптор файла три.Таким образом, 4> & -
гарантирует, что внутренняя составная команда не унаследует дескриптор файла четыре, а 3> & -
не будет наследовать дескриптор файла три, поэтому command1 получит ' более чистая, более стандартная среда. Вы также можете переместить внутренний 4> & -
рядом с 3> & -
, но я думаю, почему бы просто не ограничить его область действия насколько это возможно.
Я не уверен, как часто вещи используют файловый дескриптор третий и четвертый напрямую - я думаю, что в большинстве случаев программы используют системные вызовы, которые возвращают неиспользуемые в данный момент файловые дескрипторы, но иногда код записывает в файловый дескриптор 3 Я думаю, напрямую (я мог бы представить себе программу, проверяющую дескриптор файла, чтобы увидеть, открыт ли он, и использовать его, если он открыт, или вести себя соответствующим образом по-другому, если это не так). Так что последнее, вероятно, лучше всего иметь в виду и использовать в случаях общего назначения.
Alternate example for @lesmana solution, possibly simplified.
Provides logging to file if desired.
=====
$ cat z.sh
TEE="cat"
#TEE="tee z.log"
#TEE="tee -a z.log"
exec 8>&- 9>&-
{
{
{
{ #BEGIN - add code below this line and before #END
./zz.sh
echo ${?} 1>&8 # use exactly 1x prior to #END
#END
} 2>&1 | ${TEE} 1>&9
} 8>&1
} | exit $(read; printf "${REPLY}")
} 9>&1
exit ${?}
$ cat zz.sh
echo "my script code..."
exit 42
$ ./z.sh; echo "status=${?}"
my script code...
status=42
$
Следующее является дополнением к ответу @Patrik на случай, если вы не можете использовать одно из распространенных решений.
Этот ответ предполагает следующее:
$ PIPESTATUS
, ни set -o pipefail
Дополнительные предположения. Вы можете избавиться от всего, но это слишком усложняет рецепт, поэтому здесь он не рассматривается:
- Все, что вам нужно знать, это то, что все команды в PIPE имеют код выхода 0.
- Вам не нужны дополнительные информация о боковой полосе.
- Ваша оболочка ожидает возврата всех команд конвейера.
До: foo | бар | baz
, однако это возвращает только код выхода последней команды ( baz
)
Требуется: $?
не должно быть 0
(true ), если какая-либо из команд в канале завершилась ошибкой
После:
TMPRESULTS="`mktemp`"
{
rm -f "$TMPRESULTS"
{ foo || echo $? >&9; } |
{ bar || echo $? >&9; } |
{ baz || echo $? >&9; }
#wait
! read TMPRESULTS <&8
} 9>>"$TMPRESULTS" 8<"$TMPRESULTS"
# $? now is 0 only if all commands had exit code 0
Объяснение:
mktemp
. Обычно это немедленно создает файл в / tmp
необходимо для ksh
, потому что ksh
иначе не ожидает завершения всех команд конвейера. Однако обратите внимание, что есть нежелательные побочные эффекты, если присутствуют некоторые фоновые задачи, поэтому я закомментировал это по умолчанию. Если ожидание не повредит, вы можете его прокомментировать. read
возвращает false
, поэтому true
указывает на ошибку Это может использоваться как замена плагина для одного и требует только следующего:
/ proc / fd / N
. ОШИБКИ:
Этот сценарий содержит ошибку в случае, если в / tmp
не хватает места. Если вам также нужна защита от этого искусственного случая, вы можете сделать это следующим образом, однако это имеет тот недостаток, что число 0
в 000
зависит от количества команд в канал, поэтому он немного сложнее:
TMPRESULTS="`mktemp`"
{
rm -f "$TMPRESULTS"
{ foo; printf "%1s" "$?" >&9; } |
{ bar; printf "%1s" "$?" >&9; } |
{ baz; printf "%1s" "$?" >&9; }
#wait
read TMPRESULTS <&8
[ 000 = "$TMPRESULTS" ]
} 9>>"$TMPRESULTS" 8<"$TMPRESULTS"
Примечания по переносимости:
ksh
и аналогичные оболочки, которые ждут только последнюю команду канала, нуждаются в wait
без комментариев
Последний пример использует printf "% 1s" "$?"
вместо echo -n "$?"
, потому что это более переносимо. Не каждая платформа правильно интерпретирует -n
.
printf "$?"
тоже подойдет, однако printf "% 1s"
улавливает некоторые угловые случаи, если вы запускаете сценарий на какой-то действительно сломанной платформе. (Прочтите: если вы программируете в paranoia_mode = extreme
.)
FD 8 и FD 9 могут быть выше на платформах, которые поддерживают несколько цифр. AFAIR совместимая с POSIX оболочка должна поддерживать только одиночные цифры.
Был протестирован с Debian 8.2 sh
, bash
, ksh
, ash
, sash
и даже csh
(С bash по крайней мере )в сочетании с set -e
можно использовать подоболочку для явной эмуляции pipefail и выхода при ошибке канала
set -e
foo | bar
( exit ${PIPESTATUS[0]} )
rest of program
Таким образом, если foo
по какой-то причине произойдет сбой, -остальная часть программы не будет выполнена, и скрипт завершится с соответствующим кодом ошибки. (Это предполагает, что foo
печатает собственную ошибку, которой достаточно, чтобы понять причину сбоя)
Следующий ответ похож на некоторые другие (, особенно[0] и[1] )здесь, с разницей:
Также:
Расширения и ограничения:
Как вы увидите из моего выбора команд (для чтения двоичных данных и base64
), я использовал это для чтения и сохранения двоичных данных (, включая 0x0 )в переменной оболочки (естественно только в закодированном виде ). Если это нужно будет обработать позже, его нужно будет снова декодировать (, например. printf '%s' "${stdout_command2}" | base64 -d
| что _что _может _обрабатывать _двоичные _данные ).
Это конструкция:
stdout_command2="$(
exec 4>&1
exitstatus_command1="$(
{
{ read_binary 3>&- ; printf $? >&3 ; } 4>&- | \
base64 -w 0 >&4 3>&- 4>&- ;
} 3>&1
)" 4>&1
exitstatus_command2=$? # this must come directly after the above command substitution
exec 4>&-
if [ "${exitstatus_command1}" -ne 0 ]; then
exit 11
fi
if [ "${exitstatus_command2}" -ne 0 ]; then
exit 22
fi
)"
overall_exitstatus=$? # this would be e.g. `11` or `22` if either of the two commands failed
Кажется, это работает в dash
, bash
и busybox
в sh
соответственно ash
.
exec 4>&1
(и последующее закрытие с помощьюexec 4>&-
)необходимо, чтобы соответствовать POSIX -ly правильно:
Для простой команды без командного слова (, которого нет в строке exitstatus_command1=…
, которая является просто назначением ), POSIX оставляет порядок присвоения переменных и перенаправления открытым (, см. 2.9. 1 Простые команды:«В приведенном выше списке порядок шагов 3 и 4 может быть обратным, если на шаге 2 не получается имя команды…» ).
Например, dash
и busybox
sh
соответственно ash
, кажется, выполняют перенаправление перед назначением (, которое, таким образом, работает без exec
),в то время как bash
выполняет это после (, что, таким образом, терпит неудачу безexec
).
Я также закрываю файловые дескрипторы 3 и 4 там, где они не нужны:
3
, с 3>&-
дляread_binary
4
, с 4>&-
в целом{ read_binary 3>&- ; printf $? >&3 ; }
3
и 4
, с 3>&- 4>&-
для base64 -w 0
, но только после того, как1
был перенаправлен на 4
с >&4
(, поэтому порядок имеет значение)Вы можете проверить, какие файловые дескрипторы видны командам, заключив их в дополнительные сценарии оболочки, подобные этому:
#!/bin/sh
find /proc/$$/fd -type l | sort -V | sed 's/^/before /' >&2
base64 -w 0
e=$?
find /proc/$$/fd -type l | sort -V | sed 's/^/after /' >&2
exit $e
У меня по некоторым причинам (, которые я не совсем понял )это не сработало, оборачивая их в функции оболочки, выполняемые в той же оболочке, что и конструкция выше. В этом случае я никогда не использую все дополнительные файловые дескрипторы.
И последнее, но не менее важное, т.е.read_binary
(илиbase64
)вполне могут быть функциями (, выполняемыми в той же оболочке ), например:
read_binary()
{
if [ -z "${foo}" ]
some_more_complext_read"${path}"
if [ $? -ne 0 ]; then
return 1
#exit 1 # exit wouldn't work here
fi
else
cat -- "${path}"
if [ $? -ne 0 ]; then
return 1
#exit 2 # exit wouldn't work here
fi
fi
}
Но имейте в виду , что нельзя использовать exit
в такой функции (с приведенной выше конструкцией ), так как это не просто оставило бы функцию, но всю подоболочку из соответствующей командной подстановки($(…)
).
И exit
в либо read_binary
, либо base64
(, если это функция ), может привести к тому, что printf $? >&3
никогда не произойдет, и, таким образом, статус выхода будет потерян.
pipestatus
в zsh. К сожалению, другие оболочки не имеют этой функции. – Gilles 'SO- stop being evil' 03.06.2011, 00:05echo "$pipestatus[1]" "$pipestatus[2]"
. – Christoph Wurm 14.11.2011, 16:09if [ `echo "${PIPESTATUS[@]}" | tr -s ' ' + | bc` -ne 0 ]; then echo FAIL; fi
– l0b0 25.09.2012, 18:39