Ошибки конвейера в мою функцию приводят к игнорированию команды после 1 ошибки

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

Задействованы две формы POSIX:

2.7.2 Redirecting Output

The two general formats for redirecting output are:

[n]>word

[n]>|word

where the optional n represents the file descriptor number. If the number is omitted, the redirection shall refer to standard output (file descriptor 1).

Output redirection using the '>' format shall fail if the noclobber option is set (see the description of set -C) and the file named by the expansion of word exists and is a regular file. Otherwise, redirection using the '>' or ">|" formats shall cause the file whose name results from the expansion of word to be created and opened for output on the designated file descriptor, or standard output if none is specified. If the file does not exist, it shall be created; otherwise, it shall be truncated to be an empty file after being opened.

-

2.7.6 Duplicating an Output File Descriptor

The redirection operator:

[n]>&word

shall duplicate one output file descriptor from another, or shall close one. If word evaluates to one or more digits, the file descriptor denoted by n, or standard output if n is not specified, shall be made to be a copy of the file descriptor denoted by word; if the digits in word do not represent a file descriptor already open for output, a redirection error shall result; see Consequences of Shell Errors. If word evaluates to '-', file descriptor n, or standard output if n is not specified, is closed. Attempts to close a file descriptor that is not open shall not constitute an error. If word evaluates to something else, the behavior is unspecified.

Поэтому:

Function      POSIX-compat    POSIX 
2>&-          Yes             close 
2>/dev/null   Yes             redir
2>&1          Yes             dup 
|&            No              
&>/dev/null   No
>/dev/null    Yes             redir
>&/dev/null   ?               ?dup

Последней строки нет в исходном вопросе, но в bash она работает без нареканий. (Также работает с заменой /dev/tty на /dev/null ).

1
23.03.2020, 09:49
1 ответ

Виновником является ваша logфункция. Что он делает, так это :читает одну строку¹, и, если строка не пуста, печатает метку времени, а затем содержимое строки. Это все, что он делает :как только он обработает одну строку, он вернется.

Когда cpвыдает первое сообщение об ошибке, функция logсчитывает его и обрабатывает. Поскольку затем функция logвозвращает значение, процесс на правой -стороне канала завершается, что приводит к закрытию конца канала для чтения. Когда cpвыдает второе сообщение об ошибке, он пытается записать в закрытый канал, что приводит к его смерти из-за сигнала SIGPIPE . Стандартной ошибкой является строка -, буферизованная (по умолчанию, и cpне пытается изменить это ), поэтому буферизация не вступает в игру.

Чтобы обработать все строки ввода, вам нужно readв цикле.

log () {
    while IFS= read -r IN; do
        echo "$datetime"$'\t'"$IN"
    done | tee -a logfile >&2
}

Я также исправил вызов readдляIFS= read -rдля фактического чтения одной строки.Я удалил специальную обработку пустых строк, которая не имеет смысла (во вводе не будет пустых строк ). Я предполагаю, что вы вставили его для обработки случая, когда ввод пуст (ноль строк ввода ), но правильный способ справиться с этим — проверить статус возврата команды read. Я также исправил logдля печати стандартной ошибки, так как он используется для обработки сообщений об ошибках.

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

Имейте в виду, что размещение команды на левой -стороне конвейера имеет существенный недостаток:— это приводит к игнорированию статуса выхода . Так что, если cpне удастся, ваш сценарий продолжит успешно работать. Ошибки будут где-то логироваться, но последующие команды будут выполняться нормально и ничто не предупредит вас о том, что вам следует пойти и почитать логи. В bash, ksh или zsh вы можете установить параметрpipefailв дополнение к set -e, чтобы ваш сценарий завершался со статусом ошибки, как только какая-либо команда терпит неудачу, даже на левой -стороне трубопровод.

set -o errexit -o pipefail
copy … |& log

В качестве альтернативы можно использовать подстановку процесса вместо канала для передачи вывода ошибок через другой процесс. Замещение процесса имеет несколько иные предостережения, чем каналы; ошибки в logфактически игнорируются, и команда может вернуться до того, как logзавершится (в bash, logзапустится в фоновом режиме ).

set -e
copy … 2> >(log)

¹ Почти, вам понадобится IFS= read -r IN, чтобы никогда больше не читать и, возможно, не исказить строку.

2
28.04.2021, 23:19

Теги

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