Как записать stderr в файл, используя "тройник" с трубой?

Как записать stderr в файл, используя "тройник" с трубой?

28.03.2009 04:53:04 Просмотров 26 Источник

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

./aaa.sh | tee bbb.out

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

У вопроса есть решение - Посмотреть?

https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe#comment505605_692000
Чтобы уточнить - вы хотите, чтобы stderr перешел на экран, а также на файл?
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe#comment506529_692000
Я сделал, я отредактирую свой пост, чтобы прояснить это. Я верю, что решения лхуната будет достаточно. Спасибо всем за помощь!

Ответы - Как записать stderr в файл, используя "тройник" с трубой? / How do I write stderr to a file while using "tee" with a pipe?

Josh Kelley

28.03.2009 04:57:50

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

./aaa.sh 2>ССС.выход / тройник ./ГЭБ.из

EDIT: чтобы отобразить как 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
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/692009#comment505612_692009
Я ожидаю, что пользователь хочет, чтобы stderr пошел в их консоль в дополнение к файлу, хотя это не было явно указано.
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/692009#comment506522_692009
Мне следовало бы выразиться яснее, я ведь тоже хотел, чтобы на экране появился стдерр. Я все еще наслаждался решением Джоша Келли, но нашел решение лхуната, чтобы удовлетворить мои потребности больше. Спасибо, ребята!
ChristopheD

28.03.2009 05:00:05

При использовании 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

Является ответом!
lhunath

28.03.2009 10:54:59

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

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

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

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, мы хотим перенаправить teeс стандартный вывод в поток stderr наши снова. Затем мы используем перенаправление файлов, чтобы перенаправить command'S STDERR на вход FIFO (tee' s STDIN).

Видишь 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"
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/692407#comment8599602_692407
Я попробовал это: $ echo "HANG" > >(tee stdout.log) 2> >(tee stderr.log >&2)который работает, но ждет ввода. Есть ли простая причина, почему это происходит?
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/692407#comment8613016_692407
@Justin: я не знаю, что вы подразумеваете под"ожидает ввода". Команда, которую вы дали, ничего со мной не "ждет".
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/692407#comment23841586_692407
@lhunath, я не использую tcsh для программирования, но спасибо за подробный обзор его недостатков.
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/692407#comment27049176_692407
Кажется, я делаю что-то не так. Я использую tee in | /bin/bash > >(tee out) 2> >(tee err >&2), но печатать echo test; exit не показывают ни командной строки, ни выхода. это правильно, но подстраховаться не является ни эхом, ни в журнал... может быть, есть идеи?
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/692407#comment27068208_692407
@SillyFreak я не понимаю, что вы хотите сделать или в чем проблема, которую вы испытываете. echo test; exit не производит никакого вывода на stdout, поэтому err останется пустым.
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/692407#comment27147669_692407
спасибо за этот комментарий; я понял, что моя логическая ошибка была впоследствии: при вызове в качестве интерактивной оболочки, bash печатает командную строку и эхом отзывает exit в stderr. Однако, если стандартный поток ошибок перенаправляется, колотить начинает, как неинтерактивных по умолчанию; сравнивать /bin/bash 2> err и /bin/bash -i 2> err
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/692407#comment44707349_692407
И для тех, кто "увидел-поверил", быстрый тест: (echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/692407#comment99711528_692407
Похоже, Баш украл функцию "разветвления трубопровода"у rc, оболочки Plan 9.
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/692407#comment100016008_692407
Ух ты . Хороший ответ: "Давайте разделим его и объясним" +1
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/692407#comment102328219_692407
Для тех, кто по каким-то причинам вынужден выполнять скрипт в классической shinline следующим образом: bash
user542833

09.08.2011 09:02:55

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

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

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

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

./aaa.sh |& tee -a log
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/6991563#comment10036367_6991563
Это прекрасно работает, если вы хотите, чтобы stdout (канал 1) и stderr (канал 2) регистрировались в одном файле (один файл, содержащий смесь stdout и sterr). Другое, более сложное решение позволяет разделить stdout и stderr на 2 разных файла (stdout.журнал и stderr.лог, соответственно). Иногда это важно, иногда нет.
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/6991563#comment10257623_6991563
Другие решения во многих случаях гораздо сложнее, чем это необходимо. Этот вариант идеально подходит для меня.
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/6991563#comment25208213_6991563
Проблема с этим методом заключается в том, что вы теряете код выхода/состояния из aaa.sh процесс, который может быть важным (например, при использовании в файле makefile). У вас нет этой проблемы с принятым ответом.
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/6991563#comment27350095_6991563
если вы не возражаете против слияния stdout/stderr, то ./aaa.sh |& tee aaa.logработает (в Баш).
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/6991563#comment31073716_6991563
@ J. F. Sebastian-bash: syntax error near unexpected token '&'
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/6991563#comment31074149_6991563
@Heartinpiece: не должно быть никакого пространства между |и &
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/6991563#comment31078095_6991563
@J. F. Sebastian./Bulldozer.py |& testвсе еще не работает GNU bash, version 3.2.51(1)-release (x86_64-apple-darwin13)
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/6991563#comment31324949_6991563
Спасибо @J. F. Sebastian, что прояснили проблему :)
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/6991563#comment31598504_6991563
|&был официально добавлен в 4 как "синоним" для 2>&1 |( wiki.bash-hackers.org/bash4 )
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/6991563#comment55614235_6991563
@Stefaan я считаю, что вы можете сохранить статус выхода, если вы предваряете командную цепочку с set -o pipefailследует ;или &&, если я не ошибаюсь.
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/6991563#comment98595687_6991563
Если вы хотите регистрировать только ошибки: ./aaa.sh 2>&1>/dev/нуль | тройник -это ошибки.бревно
Gilles &#39;SO- stop being evil&#39;

Другими словами, вы хотите направить 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

Смотрите также, Как создать стандартный поток ошибок grep (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отсюда . В этом случае используйте вместо этого файловый дескриптор juggling или ksh/zsh.

https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/14737103#comment54174896_14737103
Это кажется единственным ответом, который позволяет сохранить потоки stdout/stderr как есть (например. не сливая их). Мило!
Anthony

09.10.2013 10:21:29

Это может быть полезно для людей, которые находят это через 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}
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/19279694#comment62223462_19279694
Нет, и я думаю, что исполнителю не помешает кое-что объяснить. exec >означает, переместить целевой файловый дескриптор в определенное место назначения. Значение по умолчанию равно 1, поэтому exec > /dev/nullперемещает выходные данные stdout в /dev/null с этого момента в этом сеансе. Текущие файловые дескрипторы для этого сеанса можно просмотреть, выполнив ls -l /dev/fd/. Попробуй! Затем посмотрите, что происходит, когда вы выпускаете exec 2>/tmp/stderr.log.Кроме того, exec 3>&1означает, что необходимо создать новый файловый дескриптор с номером 3 и перенаправить его на целевой файловый дескриптор 1. В этом примере целью был экран, когда была выдана команда.
https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/19279694#comment105287448_19279694
Пример, чтобы показать stdout и stderr как в экране, так и в отдельных файлах, является удивительным!!! Большое спасибо!
haridsv

31.05.2017 03:41:38

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

cmd > log 2>&1

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

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

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

shuva

01.08.2017 02:01:01

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

# 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.

https://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe/45426547#comment103824612_45426547
Отлично! Жаль, что это недооценено, потому что это хороший вариант. Кстати, у меня есть КШ :)
Arminius

27.01.2020 09:42:02

Если вы используете zsh, вы можете использовать несколько перенаправлений, так что вам даже не нужно tee:

./cmd 1>&1 2>&2 1>out_file 2>err_file

Здесь вы просто перенаправляете каждый поток на себя и целевой файл.


Полный пример

% (echo "out"; echo "err">/dev/stderr) 1>&1 2>&2 1>/tmp/out_file 2>/tmp/err_file
out
err
% cat /tmp/out_file
out
% cat /tmp/err_file
err

Обратите внимание, что для этого требуется MULTIOS

Проанализировать имплицитные MULTIOSS или teeс когда несколько перенаправлений попыток (см. перенаправление).

Помочь в развитии проекта:
Закрыть X