Чтобы уточнить - вы хотите, чтобы stderr шел на экран вместе с файлом?
Чарльз Даффи
Я сделал, я буду редактировать свой пост, чтобы уточнить это. Я верю, что решения Луната будет достаточно. Спасибо всем за помощь!
jparanich
Ответы:
786
Я предполагаю, что вы все еще хотите видеть 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, мы хотим снова перенаправить teeSTDOUT на наш STDERR. Затем мы используем перенаправление файлов для перенаправления commandSTDERR на вход FIFO (tee STDIN).
Замена процесса - это одна из тех приятных вещей, которые вы получаете в качестве бонуса выбора в bashкачестве оболочки (в отличие от shPOSIX или 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"
Я попробовал это: $ echo "HANG" > >(tee stdout.log) 2> >(tee stderr.log >&2)который работает, но ждет ввода. Есть ли простая причина, почему это происходит?
Джастин
1
@SillyFreak Я не понимаю, что ты хочешь делать или в чем проблема. эхо-тест; Выход не производит никакого вывода на стандартный вывод, поэтому ошибка останется пустой.
июня
1
спасибо за этот комментарий; Я выяснил, в чем заключалась моя логическая ошибка: при вызове в качестве интерактивной оболочки bash печатает командную строку и выводит сообщение о выходе в stderr. Однако если stderr перенаправлен, bash по умолчанию запускается как неинтерактивный; сравнить /bin/bash 2> errи/bin/bash -i 2> err
Silly Freak
15
А для тех, кто «видит, значит верить», быстрый тест:(echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
Мэтью Уилкоксон
2
Ничего себе . Хороший ответ: «Давайте разбить его и объяснить» +1
jalanb
674
почему не просто
./aaa.sh 2>&1| tee -a log
Это просто перенаправляет stderrна stdout, так что эхо как для входа и на экран. Может быть, я что-то упустил, потому что некоторые другие решения кажутся действительно сложными.
Примечание: начиная с bash версии 4, вы можете использовать |&как сокращение для 2>&1 |:
Это отлично работает, если вы хотите, чтобы и stdout (канал 1), и stderr (канал 2) были зарегистрированы в одном и том же файле (один файл, содержащий смесь как stdout, так и sterr). Другое, более сложное решение позволяет разделить stdout и stderr на 2 разных файла (stdout.log и stderr.log соответственно). Иногда это важно, иногда нет.
Тайлер Рик
19
Другие решения намного сложнее, чем это необходимо во многих случаях. Этот работает отлично для меня.
dkamins
13
Проблема этого метода заключается в том, что вы теряете код выхода / состояния из процесса aaa.sh, что может быть важно (например, при использовании в make-файле). У вас нет этой проблемы с принятым ответом.
Стефаан
9
если вы не возражаете против слияния stdout / stderr, то ./aaa.sh |& tee aaa.logработает (в bash).
JFS
5
@ Stefan Я полагаю, что вы можете сохранить статус выхода, если вы добавите цепочку команд с set -o pipefailпоследующим ;или &&если я не ошибаюсь.
Дэвид
59
Это может быть полезно для людей, которые находят это через 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}
Нет, и я думаю, что exec может использовать некоторые объяснения. exec >означает, переместить цель дескриптора файла в определенное место назначения. По умолчанию установлено значение 1, поэтому exec > /dev/nullтеперь в этом сеансе вывод stdout в / dev / null перемещается. Текущие файловые дескрипторы для этого сеанса можно увидеть, выполнив ls -l /dev/fd/. Попробуй это! Затем посмотрим, что произойдет, когда вы выполните команду « exec 2>/tmp/stderr.log.Дополнительно», exec 3>&1значит, создайте новый файловый дескриптор с номером 3 и перенаправьте его на цель файлового дескриптора 1. В этом примере целью был экран, когда была выполнена команда.
барабанный огонь
Пример показа stdout и stderr как на экране, так и в отдельных файлах - это круто !!! Большое спасибо!
Марчелло де Сейлс
21
Чтобы перенаправить 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
Я ожидаю, что пользователь хочет, чтобы stderr шел к своей консоли в дополнение к файлу, хотя это явно не было указано.
Чарльз Даффи
2
Я должен был быть более ясным, я действительно хотел, чтобы stderr на экран тоже. Мне все еще нравилось решение Джоша Келли, но я нашел решение lhunath, которое больше соответствует моим потребностям. Спасибо, парни!
jparanich
18
Другими словами, вы хотите направить stdout в один фильтр ( tee bbb.out) и stderr в другой фильтр ( tee ccc.out). Не существует стандартного способа передачи чего-либо, кроме stdout, в другую команду, но вы можете обойти это, манипулируя дескрипторами файлов.
{{./aaa.sh | tee bbb.out;}2>&11>&3| tee ccc.out;}3>&11>&2
В 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.
Это кажется единственным ответом, который позволяет сохранить потоки stdout / stderr как есть (например, не объединяя их). Сладкий!
user1338062
Самый простой подход sh, полезный для заданий cron, где замена процесса недоступна.
Роджер Дуек
13
Если вы используете 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
В моем случае скрипт выполнял команду, перенаправляя как stdout, так и stderr в файл, что-то вроде:
cmd > log 2>&1
Мне нужно было обновить его таким образом, чтобы в случае сбоя предпринять некоторые действия на основе сообщений об ошибках. Я мог бы, конечно, удалить dup 2>&1и захватить stderr из скрипта, но тогда сообщения об ошибках не попадут в файл журнала для справки. Хотя принятый ответ от @lhunath должен делать то же самое, он перенаправляет stdoutи stderrв разные файлы, что не то, что я хочу, но помогло мне найти точное решение, которое мне нужно:
(cmd 2>>(tee /dev/stderr))> log
С учетом указанных выше, журнал будет иметь копию как stdoutи stderrя могу захватить stderrиз моего сценария без необходимости беспокоиться о stdout.
Следующее будет работать для 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>&11>&3| tee -a stderr.log 1>&3# cleanup collector
exec 3>&-
Реальный трюк здесь, представляет собой последовательность из 2>&1 1>&3которых в нашем случае перенаправляет stderrк stdoutи перенаправляет stdoutк дескриптору 3. На данный момент stderrи stdoutеще не объединены.
По сути, stderr(as stdin) передается туда, teeгде он регистрируется, stderr.logа также перенаправляет на дескриптор 3.
И дескриптор 3записывает это combined.logвсе время. Таким образом, combined.logсодержит как stdoutи stderr.
Ответы:
Я предполагаю, что вы все еще хотите видеть STDERR и STDOUT на терминале. Вы могли бы пойти за ответом Джоша Келли, но я нахожу
tail
в тени фон, который выводит ваш лог-файл очень хакерский и грязный. Обратите внимание, как вам нужно сохранить exra FD и выполнить очистку после этого, убив его, и технически это следует делать вtrap '...' EXIT
.Существует лучший способ сделать это, и вы уже открыли для себя это:
tee
.Только вместо того, чтобы использовать его для своего стандартного вывода, используйте тройник для стандартного вывода и один для стандартного вывода. Как ты это сделаешь? Замена процесса и перенаправление файлов:
Давайте разделим это и объясним:
>(...)
(процесс подстановки) создает FIFO и позволяетtee
прослушивать его. Затем он использует>
(перенаправление файлов) для перенаправления STDOUTcommand
в FIFO, которыйtee
прослушивает ваш первый .То же самое для второго:
Мы снова используем подстановку процессов, чтобы создать
tee
процесс, который читает из STDIN и выгружает его вstderr.log
.tee
выводит свой ввод обратно на STDOUT, но так как его ввод - наш STDERR, мы хотим снова перенаправитьtee
STDOUT на наш STDERR. Затем мы используем перенаправление файлов для перенаправленияcommand
STDERR на вход FIFO (tee
STDIN).Смотрите http://mywiki.wooledge.org/BashGuide/InputAndOutput
Замена процесса - это одна из тех приятных вещей, которые вы получаете в качестве бонуса выбора в
bash
качестве оболочки (в отличие отsh
POSIX или Bourne).Во-
sh
первых, вам придется делать вещи вручную:источник
$ echo "HANG" > >(tee stdout.log) 2> >(tee stderr.log >&2)
который работает, но ждет ввода. Есть ли простая причина, почему это происходит?/bin/bash 2> err
и/bin/bash -i 2> err
(echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
почему не просто
Это просто перенаправляет
stderr
наstdout
, так что эхо как для входа и на экран. Может быть, я что-то упустил, потому что некоторые другие решения кажутся действительно сложными.Примечание: начиная с bash версии 4, вы можете использовать
|&
как сокращение для2>&1 |
:источник
./aaa.sh |& tee aaa.log
работает (в bash).set -o pipefail
последующим;
или&&
если я не ошибаюсь.Это может быть полезно для людей, которые находят это через Google. Просто раскомментируйте пример, который вы хотите попробовать. Конечно, не стесняйтесь переименовывать выходные файлы.
источник
exec >
означает, переместить цель дескриптора файла в определенное место назначения. По умолчанию установлено значение 1, поэтомуexec > /dev/null
теперь в этом сеансе вывод stdout в / dev / null перемещается. Текущие файловые дескрипторы для этого сеанса можно увидеть, выполнивls -l /dev/fd/
. Попробуй это! Затем посмотрим, что произойдет, когда вы выполните команду «exec 2>/tmp/stderr.log.
Дополнительно»,exec 3>&1
значит, создайте новый файловый дескриптор с номером 3 и перенаправьте его на цель файлового дескриптора 1. В этом примере целью был экран, когда была выполнена команда.Чтобы перенаправить stderr в файл, отобразите стандартный вывод на экран, а также сохраните стандартный вывод в файл:
РЕДАКТИРОВАТЬ : Чтобы вывести на экран и stderr, и stdout, а также сохранить их в файл, вы можете использовать перенаправление ввода / вывода bash :
источник
Другими словами, вы хотите направить stdout в один фильтр (
tee bbb.out
) и stderr в другой фильтр (tee ccc.out
). Не существует стандартного способа передачи чего-либо, кроме stdout, в другую команду, но вы можете обойти это, манипулируя дескрипторами файлов.Смотрите также Как получить стандартный поток ошибок (stderr)? и когда бы вы использовали дополнительный файловый дескриптор?
В bash (и ksh и zsh), но не в других оболочках POSIX, таких как dash, вы можете использовать подстановку процессов :
Помните, что в bash эта команда возвращается, как только
./aaa.sh
завершается, даже еслиtee
команды все еще выполняются (ksh и zsh ожидают подпроцессов). Это может быть проблемой, если вы делаете что-то подобное./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out
. В этом случае вместо этого используйте жонглирование дескриптора файла или ksh / zsh.источник
sh
, полезный для заданий cron, где замена процесса недоступна.Если вы используете bash:
Кредит (не отвечая из головы) идет здесь: http://www.cygwin.com/ml/cygwin/2003-06/msg00772.html
источник
В моем случае скрипт выполнял команду, перенаправляя как stdout, так и stderr в файл, что-то вроде:
Мне нужно было обновить его таким образом, чтобы в случае сбоя предпринять некоторые действия на основе сообщений об ошибках. Я мог бы, конечно, удалить dup
2>&1
и захватить stderr из скрипта, но тогда сообщения об ошибках не попадут в файл журнала для справки. Хотя принятый ответ от @lhunath должен делать то же самое, он перенаправляетstdout
иstderr
в разные файлы, что не то, что я хочу, но помогло мне найти точное решение, которое мне нужно:С учетом указанных выше, журнал будет иметь копию как
stdout
иstderr
я могу захватитьstderr
из моего сценария без необходимости беспокоиться оstdout
.источник
Следующее будет работать для KornShell (ksh), где замена процесса недоступна,
Реальный трюк здесь, представляет собой последовательность из
2>&1 1>&3
которых в нашем случае перенаправляетstderr
кstdout
и перенаправляетstdout
к дескриптору3
. На данный моментstderr
иstdout
еще не объединены.По сути,
stderr
(asstdin
) передается туда,tee
где он регистрируется,stderr.log
а также перенаправляет на дескриптор 3.И дескриптор
3
записывает этоcombined.log
все время. Таким образом,combined.log
содержит какstdout
иstderr
.источник
Если вы используете zsh , вы можете использовать несколько перенаправлений, поэтому вам даже не нужно
tee
:Здесь вы просто перенаправляете каждый поток на себя и целевой файл.
Полный пример
Обратите внимание, что для этого требуется установить
MULTIOS
параметр (который используется по умолчанию).источник