Как мне написать stderr в файл при использовании "тройника" с каналом? - PullRequest
477 голосов
/ 28 марта 2009

Я знаю, как использовать tee, чтобы записать вывод (STDOUT) aaa.sh в bbb.out, при этом отображая его в терминале:

./aaa.sh | tee bbb.out

Как бы мне теперь записать STDERR в файл с именем ccc.out, пока он все еще отображается?

Ответы [ 8 ]

704 голосов
/ 28 марта 2009

Я предполагаю, что вы все еще хотите видеть STDERR и STDOUT на терминале. Вы могли бы пойти за ответом Джоша Келли, но я нахожу в фоновом режиме tail, который выводит ваш лог-файл очень хакерским и грязным. Обратите внимание, как вам нужно сохранить exra FD и выполнить очистку после этого, убив его, и технически это должно быть сделано в trap '...' EXIT.

Есть лучший способ сделать это, и вы уже обнаружили это: tee.

Только вместо того, чтобы использовать его для своего стандартного вывода, используйте тройник для стандартного вывода и один для стандартного вывода. Как ты это сделаешь? Замена процесса и перенаправление файлов:

command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)

Давайте разберем это и объясним:

> >(..)

>(...) (процесс подстановки) создает FIFO и позволяет tee прослушивать его. Затем он использует > (перенаправление файлов) для перенаправления STDOUT command на FIFO, который прослушивает ваш первый tee.

То же самое для второго:

2> >(tee -a stderr.log >&2)

Мы снова используем подстановку процессов, чтобы создать tee процесс, который читает из STDIN и выводит его в stderr.log. tee выводит свой вход обратно в STDOUT, но так как его ввод - наш STDERR, мы хотим перенаправить STDOUT tee в наш STDERR снова. Затем мы используем перенаправление файлов для перенаправления STDERR command на вход FIFO (STDIN tee).

См. http://mywiki.wooledge.org/BashGuide/InputAndOutput

Замена процесса - это одна из тех приятных вещей, которые вы получаете в качестве бонуса, выбирая bash в качестве оболочки в отличие от sh (POSIX или Bourne).


В sh вам придется делать что-то вручную:

out="${TMPDIR:-/tmp}/out.$$" err="${TMPDIR:-/tmp}/err.$$"
mkfifo "$out" "$err"
trap 'rm "$out" "$err"' EXIT
tee -a stdout.log < "$out" &
tee -a stderr.log < "$err" >&2 &
command >"$out" 2>"$err"
596 голосов
/ 09 августа 2011

почему бы не просто:

./aaa.sh 2>&1 | tee -a log

Это просто перенаправляет от stderr до stdout, поэтому эхо-сигналы отображаются как в журнале, так и на экране. Может быть, я что-то упускаю, потому что некоторые другие решения кажутся действительно сложными.

Примечание: Начиная с bash версии 4, вы можете использовать |& как сокращение для 2>&1 |:

./aaa.sh |& tee -a log
52 голосов
/ 09 октября 2013

Это может быть полезно для людей, которые находят это через Google. Просто раскомментируйте пример, который вы хотите попробовать. Конечно, не стесняйтесь переименовывать выходные файлы.

#!/bin/bash

STATUSFILE=x.out
LOGFILE=x.log

### All output to screen
### Do nothing, this is the default


### All Output to one file, nothing to the screen
#exec > ${LOGFILE} 2>&1


### All output to one file and all output to the screen
#exec > >(tee ${LOGFILE}) 2>&1


### All output to one file, STDOUT to the screen
#exec > >(tee -a ${LOGFILE}) 2> >(tee -a ${LOGFILE} >/dev/null)


### All output to one file, STDERR to the screen
### Note you need both of these lines for this to work
#exec 3>&1
#exec > >(tee -a ${LOGFILE} >/dev/null) 2> >(tee -a ${LOGFILE} >&3)


### STDOUT to STATUSFILE, stderr to LOGFILE, nothing to the screen
#exec > ${STATUSFILE} 2>${LOGFILE}


### STDOUT to STATUSFILE, stderr to LOGFILE and all output to the screen
#exec > >(tee ${STATUSFILE}) 2> >(tee ${LOGFILE} >&2)


### STDOUT to STATUSFILE and screen, STDERR to LOGFILE
#exec > >(tee ${STATUSFILE}) 2>${LOGFILE}


### STDOUT to STATUSFILE, STDERR to LOGFILE and screen
#exec > ${STATUSFILE} 2> >(tee ${LOGFILE} >&2)


echo "This is a test"
ls -l sdgshgswogswghthb_this_file_will_not_exist_so_we_get_output_to_stderr_aronkjegralhfaff
ls -l ${0}
22 голосов
/ 28 марта 2009

Чтобы перенаправить stderr в файл, отобразить стандартный вывод на экран, а также сохранить стандартный вывод в файл:

./aaa.sh 2>ccc.out | tee ./bbb.out

РЕДАКТИРОВАТЬ : чтобы вывести на экран как stderr, так и stdout, а также сохранить их в файл, вы можете использовать перенаправление ввода / вывода bash :

#!/bin/bash

# Create a new file descriptor 4, pointed at the file
# which will receive stderr.
exec 4<>ccc.out

# Also print the contents of this file to screen.
tail -f ccc.out &

# Run the command; tee stdout as normal, and send stderr
# to our file descriptor 4.
./aaa.sh 2>&4 | tee bbb.out

# Clean up: Close file descriptor 4 and kill tail -f.
exec 4>&-
kill %1
15 голосов
/ 06 февраля 2013

Другими словами, вы хотите направить stdout в один фильтр (tee bbb.out) и stderr в другой фильтр (tee ccc.out). Не существует стандартного способа передачи чего-либо, кроме stdout, в другую команду, но вы можете обойти это, манипулируя дескрипторами файлов.

{ { ./aaa.sh | tee bbb.out; } 2>&1 1>&3 | tee ccc.out; } 3>&1 1>&2

См. Также Как получить стандартный поток ошибок (stderr)? и Когда бы вы использовали дополнительный файловый дескриптор?

В bash (и ksh и zsh), но не в других оболочках POSIX, таких как dash, вы можете использовать подстановка процесса :

./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out)

Помните, что в bash эта команда возвращается, как только заканчивается ./aaa.sh, даже если команды tee все еще выполняются (ksh и zsh ожидают подпроцессов). Это может быть проблемой, если вы делаете что-то вроде ./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out. В этом случае вместо этого используйте жонглирование файлового дескриптора или ksh / zsh.

12 голосов
/ 28 марта 2009

При использовании bash:

# Redirect standard out and standard error separately
% cmd >stdout-redirect 2>stderr-redirect

# Redirect standard error and out together
% cmd >stdout-redirect 2>&1

# Merge standard error with standard out and pipe
% cmd 2>&1 |cmd2

Кредит (не отвечая из головы) идет сюда: http://www.cygwin.com/ml/cygwin/2003-06/msg00772.html

2 голосов
/ 01 августа 2017

Следующее будет работать для KornShell (ksh), где замена процесса недоступна,

# create a combined(stdin and stdout) collector
exec 3 <> combined.log

# stream stderr instead of stdout to tee, while draining all stdout to the collector
./aaa.sh 2>&1 1>&3 | tee -a stderr.log 1>&3

# cleanup collector
exec 3>&-

Реальный трюк здесь - это последовательность 2>&1 1>&3, которая в нашем случае перенаправляет stderr в stdout и перенаправляет stdout в дескриптор 3. На данный момент stderr и stdout еще не объединены.

По сути, stderr (как stdin) передается в tee, где регистрируется в stderr.log, а также перенаправляется в дескриптор 3.

И дескриптор 3 постоянно записывает его в combined.log. Таким образом, combined.log содержит как stdout, так и stderr.

2 голосов
/ 31 мая 2017

В моем случае скрипт выполнял команду, перенаправляя как stdout, так и stderr в файл, что-то вроде:

cmd > log 2>&1

Мне нужно было обновить его так, чтобы в случае сбоя предпринять некоторые действия на основе сообщений об ошибках. Конечно, я мог бы удалить dup 2>&1 и захватить stderr из скрипта, но тогда сообщения об ошибках не будут попадать в файл журнала для справки. Хотя принятый ответ от @lhunath должен делать то же самое, он перенаправляет stdout и stderr на разные файлы, что не то, что я хочу, но помогло мне найти точное решение, которое мне нужно:

(cmd 2> >(tee /dev/stderr)) > log

С учетом вышесказанного, log будет иметь копию stdout и stderr, и я могу захватить stderr из моего скрипта, не беспокоясь о stdout.

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