Я знаю, как использовать tee
для записи выходных данных ( STDOUT
) aaa.sh
в bbb.out
, при этом отображая его в терминале:
./aaa.sh | tee bbb.out
Как бы мне теперь записать STDERR
файл с именем ccc.out
, пока он все еще отображается?
Я знаю, как использовать tee
для записи выходных данных ( STDOUT
) aaa.sh
в bbb.out
, при этом отображая его в терминале:
./aaa.sh | tee bbb.out
Как бы мне теперь записать STDERR
файл с именем ccc.out
, пока он все еще отображается?
Ответы:
Я предполагаю, что вы все еще хотите видеть 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, мы хотим снова перенаправить tee
STDOUT на наш STDERR. Затем мы используем перенаправление файлов для перенаправления command
STDERR на вход FIFO (tee
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"
$ 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)
почему не просто
./aaa.sh 2>&1 | tee -a log
Это просто перенаправляет stderr
на stdout
, так что эхо как для входа и на экран. Может быть, я что-то упустил, потому что некоторые другие решения кажутся действительно сложными.
Примечание: начиная с bash версии 4, вы можете использовать |&
как сокращение для 2>&1 |
:
./aaa.sh |& tee -a log
./aaa.sh |& tee aaa.log
работает (в bash).
set -o pipefail
последующим ;
или &&
если я не ошибаюсь.
Это может быть полезно для людей, которые находят это через 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 >
означает, переместить цель дескриптора файла в определенное место назначения. По умолчанию установлено значение 1, поэтому exec > /dev/null
теперь в этом сеансе вывод stdout в / dev / null перемещается. Текущие файловые дескрипторы для этого сеанса можно увидеть, выполнив ls -l /dev/fd/
. Попробуй это! Затем посмотрим, что произойдет, когда вы выполните команду « exec 2>/tmp/stderr.log.
Дополнительно», exec 3>&1
значит, создайте новый файловый дескриптор с номером 3 и перенаправьте его на цель файлового дескриптора 1. В этом примере целью был экран, когда была выполнена команда.
Чтобы перенаправить 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
Другими словами, вы хотите направить 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.
sh
, полезный для заданий cron, где замена процесса недоступна.
Если вы используете 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
В моем случае скрипт выполнял команду, перенаправляя как 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>&1 1>&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
.
Если вы используете 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
параметр (который используется по умолчанию).
MULTIOS
Выполнять неявные
tee
s илиcat
s при попытке нескольких перенаправлений (см. Перенаправление ).