Подтвердить что ты не робот

Информировать правую сторону трубопровода о неисправности слева?

Я полюбил использование генераторного шаблона между функциями в моих сценариях оболочки. Что-то вроде этого:

parse_commands /da/cmd/file | process_commands

Однако основная проблема с этим шаблоном заключается в том, что если parse_command встречает ошибку, единственный способ, которым я обнаружил, чтобы уведомить Process_command о том, что это не удалось, - это явно сказать это (например, echo "FILE_NOT_FOUND" ). Это означает, что каждая потенциально неисправная операция в parse_command должна быть ограждена.

Нет ли способа, с помощью которого process_command может обнаружить, что левая сторона вышла с ненулевым кодом выхода?

4b9b3361

Ответ 1

Продолжается ли процесс труб, даже если первый процесс завершился, или проблема в том, что у вас нет способа узнать, что первый процесс не удалось?

Если это последний, вы можете посмотреть на переменную PIPESTATUS (которая на самом деле является массивом BASH). Это даст вам код выхода первой команды:

parse_commands /da/cmd/file | process_commands
temp=("${PIPESTATUS[@]}")
if [ ${temp[0]} -ne 0 ]
then
    echo 'parse_commands failed'
elif [ ${temp[1]} -ne 0 ]
then
    echo 'parse_commands worked, but process_commands failed'
fi

В противном случае вам придется использовать совлокальные процессы.

Ответ 2

Используйте set -o pipefail поверх bash script, чтобы при выходе из строя левой части канала (статус выхода!= 0) правая сторона не выполняет.

Ответ 3

В отличие от оператора и оператора (&), оператор трубы (|) работает путем нереста обоих процессов одновременно, поэтому первый процесс может передавать свой вывод во второй процесс без необходимости буферизации промежуточных данных. Это позволяет обрабатывать большие объемы данных с небольшим объемом памяти или диска.

Следовательно, статус выхода первого процесса не будет доступен для второго, пока он не завершится.

Ответ 4

Вы можете попробовать некоторые работы arround, используя fifo:

mkfifo /tmp/a
cat /tmp/a | process_commands &

parse_cmd /da/cmd/file > /tmp/a || (echo "error"; # kill process_commands)

Ответ 5

У меня недостаточно репутации, чтобы комментировать, но в принятом ответе отсутствовала закрывающая } в строке 5.

После исправления кода код будет вызывать ошибку -ne: unary operator expected, которая указывает на проблему: PIPESTATUS перезаписывается условным выражением, следующим за командой if, поэтому возвращаемое значение process_commands никогда не будет проверено

Это связано с тем, что [ ${PIPESTATUS[0]} -ne 0 ] эквивалентен test ${PIPESTATUS[0]} -ne 0, который меняет $PIPESTATUS так же, как и на любую другую команду. Например:

return0 () { return 0;}
return3 () { return 3;}

return0 | return3
echo "PIPESTATUS: ${PIPESTATUS[@]}"

Это возвращает PIPESTATUS: 0 3, как ожидалось. Но что, если мы введем условные обозначения?

return0 | return3
if [ ${PIPESTATUS[0]} -ne 0 ]; then
    echo "1st command error: ${PIPESTATUS[0]}"
elif [ ${PIPESTATUS[1]} -ne 0 ]; then
    echo "2nd command error: ${PIPESTATUS[1]}"
else
    echo "PIPESTATUS: ${PIPESTATUS[@]}"
    echo "Both return codes = 0."
fi

Мы получаем ошибку [: -ne: unary operator expected, и это:

PIPESTATUS: 2
Both return codes = 0.

Чтобы исправить это, $PIPESTATUS следует сохранить в другой переменной массива, например:

return0 | return3
TEMP=("${PIPESTATUS[@]}")
echo "TEMP: ${TEMP[@]}"
if [ ${TEMP[0]} -ne 0 ]; then
    echo "1st command error: ${TEMP[0]}"
elif [ ${TEMP[1]} -ne 0 ]; then
    echo "2nd command error: ${TEMP[1]}"
else
    echo "TEMP: ${TEMP[@]}"
    echo "All return codes = 0."
fi

Какие принты:

TEMP: 0 3
2nd command error: 3

как и предполагалось.

Изменить: я исправил принятый ответ, но я оставляю это объяснение для потомков.

Ответ 6

Если у вас есть command1 && command2, тогда команда 2 будет выполнена только тогда, когда первая команда будет успешной - в противном случае произойдет бурное короткое замыкание. Одним из способов использования этой команды было бы создать первую команду (ваш parse_commands...), которая сбрасывает к временному, а затем выполнить вторую команду из этого файла.

Изменить: разумным использованием ; вы можете привести в порядок временный файл, например.

(command1 && command2) ; rm temporaryfile

Ответ 7

Есть способ сделать это в bash 4.0, который добавляет coproc встроенный из ash. Этот объект для сопроцесса заимствован из ksh, который использует другой синтаксис. Единственной оболочкой, к которой я имею доступ в своей системе, поддерживающей сопроцессоры, является ksh. Вот решение, написанное с помощью ksh:

parse_commands  /da/cmd/file |&
parser=$!

process_commands <&p &
processor=$!

if wait $parser
then
    wait $processor
    exit $?
else
    kill $processor
    exit 1
fi

Идея состоит в том, чтобы запустить parse_commands в фоновом режиме с помощью труб, соединяющих его с основной оболочкой. Pid сохраняется в parser. Затем process_commands запускается с выходом parse_commands в качестве его ввода. (Это то, что делает <&p). Это также помещается в фоновое изображение с сохраненным pid в processor.

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

Это должно быть довольно просто перевести это, чтобы использовать встроенный bash 4.0/ash coproc, но у меня нет хорошей документации и способа проверить это.

Ответ 8

Вы можете запустить parse_commands /da/cmd/file в явном подоболочке и echo статус выхода этой подоболочки через канал до process_commands, который также запускается в явном подоболочке для обработки данных в каналах, содержащихся в /dev/stdin.

Далекий от элегантности, но, похоже, выполняет эту работу:)

Простой пример:

(
( ls -l ~/.bashrcxyz; echo $? ) | 
( 
piped="$(</dev/stdin)"; 
[[ "$(tail -n 1 <<<"$piped")" -eq 0 ]] && printf '%s\n' "$piped" | sed '$d' || exit 77 
); 
echo $?
)

Ответ 9

Как насчет:

parse_commands /da/cmd/file > >(process_commands)