Статус выхода из Bash, используемый с PIPE

Благодаря ответ, который я получил на один из моих вопросов , я нашел другое решение . Итак, для полноты картины, вот решение, использующее find :

find . -path './.git' -prune -o -print |
  tar -czvf ../archive.tgz --no-recursion -T -

И если вы хотите исключить возможные папки .git внутри (sub) + folder :

find . -path '*/.git' -prune -o -print |
  tar -czvf ../archive.tgz --no-recursion -T -

Последнее также может быть достигнуто с помощью:

find . -type d -name '.git' -prune -o -print |
  tar -czvf ../archive.tgz --no-recursion -T -

Очень удобно, если вы можете использовать другие методы фильтрации из find , например дата модификации, разрешения,…

(Поскольку часто подразумеваются правильно сформированные имена файлов, в противном случае вам нужно использовать нулевые байты в качестве разделителей, что может означать -print0 для find и - null для tar , если поддерживается.)

10
20.05.2017, 16:52
4 ответа

Да, PIPE создает статус выхода; если вам нужен код выхода команды перед PIPE, вы можете использовать $PIPESTATUS

В соответствии с этими вопросами и ответами по переполнению стека, чтобы распечатать статус выхода команды при использовании канала вы можете сделать:

your_command | echo $PIPESTATUS

Или установить pipefail с помощью команды set -o pipefail(чтобы отменить его, выполните set +o pipefail) и используйте $?вместо $PIPESTATUS.

your_command | echo $?

Эти команды работают только после того, как вы запустите их хотя бы один раз; это означает, что PIPESTATUSработает только тогда, когда вы запускаете его после команды с конвейером, например:

command_which_exit_10 | command_which_exit_1
echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"

Вы получите 10 за ${PIPESTATUS[0]}и 1 за ${PIPESTATUS[1]}. Дополнительную информацию см. в этом вопросе и ответе U&L.

4
27.01.2020, 19:59

Статус выхода канала — это статус выхода правой команды. Статус выхода левой команды игнорируется.

(Обратите внимание, что what lss | echo $? этого не показывает. Вы должны запустить what lss | true; echo $?, чтобы показать это. В which lss | echo $?, echo $? сообщает о состоянии последней команды перед этим конвейером.)

Причина, по которой оболочки ведут себя таким образом, заключается в том, что существует довольно распространенный сценарий, при котором возникает ошибка. в левой части следует игнорировать. Если правая сторона выходит (или, в более общем случае, закрывает свой стандартный ввод), в то время как левая сторона все еще записывает, то левая сторона получает сигнал SIGPIPE. В этом случае обычно нет ничего плохого: правая часть не заботится о данных; если роль левой стороны состоит исключительно в том, чтобы производить эти данные, то она может остановиться.

Однако, если левая сторона умирает по какой-либо причине, отличной от SIGPIPE, или если задача левой стороны заключалась не только в создании данных на стандартном выходе, то ошибка в левой части является ошибкой. подлинная ошибка, о которой следует сообщить.

В простом sh единственным решением является использование именованного канала.

set -e
mkfifo p
command1 >p & pid1=$!
command2 <p
wait $pid1

В ksh, bash и zsh вы можете указать оболочке сделать выход конвейера с ненулевым статусом, если какой-либо компонент конвейера завершается с ненулевым статусом.Вам необходимо установить параметр pipefail:

  • ksh: set -o pipefail
  • bash: shopt -s pipefail
  • zsh: setopt pipefail ] (или setopt pipe_fail)

В mksh, bash и zsh вы можете получить статус каждого компонента конвейера, используя переменную PIPESTATUS (bash, mksh) или pipestatus (zsh), который представляет собой массив, содержащий статус всех команд в последнем конвейере (обобщение $?).

11
27.01.2020, 19:59

Статус выхода конвейера — это статус выхода последней команды в конвейере (если не указан параметр оболочки pipefail устанавливается в оболочках, которые его поддерживают, и в этом случае статус выхода будет таким же, как у последней команды в конвейере, которая завершается с ненулевым статусом).

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

Конвейер

which lss | echo $?

не имеет смысла, поскольку echo не считывает свой стандартный ввод (конвейеры используются для передачи данных между выводом одной команды и вводом следующей). echo также не будет печатать статус выхода конвейера, но статус выхода команды запускается сразу перед конвейером.

$ false
$ which hello | echo $?
1
which: hello: Command not found.

$ true
$ which hello | echo $?
0
which: hello: Command not found.

Это лучший пример:

$ echo hello | read a
$ echo $?
0

$ echo nonexistent | { read filename && rm $filename; }
rm: nonexistent: No such file or directory
$ echo $?
1

Это означает, что конвейер также может использоваться с if:

if gzip -dc file.gz | grep -q 'something'; then
  echo 'something was found'
fi
14
27.01.2020, 19:59

Оболочка оценивает командную строку перед выполнением конвейера. Итак, $? — это значение последней команды, выполненной перед конвейером. Запускается ли сам echo до или после , который не имеет значения.

Теперь, если ваш вопрос был конкретно о распространении статуса выхода, вы можете изучить поведение по умолчанию следующим образом:

% false | true
% echo $?
0

% true | false
% echo $?
1

Как видите, статус выхода конвейера — это статус выхода его последней команды.

0
27.01.2020, 19:59

Теги

Похожие вопросы