Считайте это дополнением к выбранному ответу. Вы можете захотеть узнать, какие формы соответствуют 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 ).
Виновником является ваша 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
, чтобы никогда больше не читать и, возможно, не исказить строку.