Проблема с errexit в bash

Я пишу bash-скрипт и хочу, чтобы он вылетал при первой ошибке. Однако я не могу заставить его сделать это в определенных обстоятельствах, которые я упростил ниже:

#!/bin/bash
set -Exu
bad_command() {
  false
  #exit 1
  echo "NO!!"
}
(set -o pipefail; bad_command | cat ; echo "${PIPESTATUS[@]}; $?") || false
echo "NOO!!"

Ожидаемым поведением будет сбой подоболочки bad_command, перенесенный на сбой подоболочки (), который распространится на сбой внешней оболочки. Но ни один из них не падает, и печатаются оба NO (!?)

Если я раскомментирую оператор exit 1, то NO больше не печатается, но NOO по-прежнему (!?)

Я пытался явно использовать set -e внутри каждой из трех оболочек (первая строка в функции, первый оператор после (, но никаких изменений.

Примечание. Мне нужно выполнить канал внутри подоболочки (), потому что это упрощение более сложного сценария. Без подоболочки () все работает так, как ожидалось, никаких NO ни с false, ни с exit 1.


person Matei David    schedule 13.12.2012    source источник


Ответы (2)


Похоже, это ошибка bash или даже POSIX: https://groups.google.com/forum/?fromgroups=#!topic/gnu.bash.bug/NCK_0GmIv2M

person Matei David    schedule 14.12.2012

Столкнувшись с той же проблемой, я нашел обходной путь. На самом деле 3 в зависимости от того, чего вы хотите достичь.

Сначала небольшая переработка кода примера OP, поскольку обработка кода выхода требует дополнительной работы:

#! /bin/bash
set -eEu
bad_command_extra() {
        return 42
}
bad_command() {
  bad_command_extra
  echo "NO!!"
}

if bad_command; then echo "NOO!!"; else echo "errexit worked: $?"; fi

Если нужно только, чтобы errexit работал, достаточно вызвать bad_command. Хитрость заключается в том, чтобы запустить bad_command в фоновом режиме:

(bad_command) &
bc_pid=$!
if wait $bc_pid; then echo "NOO!!"; else echo "errexit worked: $?"; fi

Если вы хотите работать и с выводом (аналогично abc=$(bad_command)), сохраните его во временном файле, как обычно:

tmp_out=$(mktemp)
tmp_err=$(mktemp)
(bad_command >$tmp_out 2>$tmp_err) &
bc_pid=$!
if wait $bc_pid; then echo "NOO!!"; else echo "errexit worked: $?"; fi
cat $tmp_out $tmp_err
rm -f $tmp_out $tmp_err

Наконец, в ходе тестирования я обнаружил, что команда wait возвращает либо 0, либо 1, но не фактический код выхода bad_command (bash 4.3.42). Это требует дополнительной работы:

tmp_out=$(mktemp)
tmp_err=$(mktemp)
tmp_exit=$(mktemp)
echo 0 > $tmp_exit
(
        get_exit () {
                echo $? > $tmp_exit
        }
        trap get_exit ERR
        bad_command >$tmp_out 2>$tmp_err
) &
bc_pid=$!
bc_exit=$(cat $tmp_exit)
if wait $bc_pid
then echo "NOO!!"
else echo "errexit worked: $bc_exit"
fi
cat $tmp_out $tmp_err
rm -f $tmp_out $tmp_err $tmp_exit

По какой-то странной причине размещение if в одной строке, как и раньше, привело к тому, что в этом случае я получил код выхода 0!

person Eric Seynaeve    schedule 29.06.2016