Я хочу перенаправить как stdout, так и stderr процесса в один файл. Как мне это сделать в Bash?
Я хочу перенаправить как stdout, так и stderr процесса в один файл. Как мне это сделать в Bash?
Ответы:
Посмотрите здесь . Должно быть:
yourcommand &>filename
(перенаправляет как stdout
и stderr
на имя файла).
#!/bin/bash
а не с #!/bin/sh
, так как в требует bash.
do_something 2>&1 | tee -a some_file
Это позволит перенаправить stderr в stdout и stdout some_file
и распечатать его в stdout.
do_something &>filename
не. +1.
Ambiguous output redirect.
почему?
$?
больше не относится к состоянию выхода do_something
, а к состоянию выхода tee
.
Вы можете перенаправить stderr в stdout, а stdout в файл:
some_command >file.log 2>&1
Видеть Http://tldp.org/LDP/abs/html/io-redirection.html.
Этот формат предпочтительнее, чем самый популярный &> формат, который работает только в bash. В оболочке Bourne это можно интерпретировать как запуск команды в фоновом режиме. Кроме того, формат более читабелен 2 (STDERR) перенаправлен на 1 (STDOUT).
РЕДАКТИРОВАТЬ: изменил порядок, как указано в комментариях
# Close STDOUT file descriptor
exec 1<&-
# Close STDERR FD
exec 2<&-
# Open STDOUT as $LOG_FILE file for read and write.
exec 1<>$LOG_FILE
# Redirect STDERR to STDOUT
exec 2>&1
echo "This line will appear in $LOG_FILE, not 'on screen'"
Теперь простое эхо запишет в $ LOG_FILE. Полезно для демонизации.
Автору оригинального поста,
Это зависит от того, что вам нужно достичь. Если вам просто нужно перенаправить вход / выход из команды, которую вы вызываете из скрипта, ответы уже даны. Мой рассказ о перенаправлении в текущем скрипте, который влияет на все команды / встроенные модули (включая вилки) после упомянутого фрагмента кода.
Другое крутое решение - это перенаправление одновременно на std-err / out и на logger или log-файл, что подразумевает разделение «потока» на две части. Эта функциональность обеспечивается командой 'tee', которая может записывать / добавлять сразу несколько файловых дескрипторов (файлов, сокетов, каналов и т. Д.): Tee FILE1 FILE2 ...> (cmd1)> (cmd2) ...
exec 3>&1 4>&2 1> >(tee >(logger -i -t 'my_script_tag') >&3) 2> >(tee >(logger -i -t 'my_script_tag') >&4)
trap 'cleanup' INT QUIT TERM EXIT
get_pids_of_ppid() {
local ppid="$1"
RETVAL=''
local pids=`ps x -o pid,ppid | awk "\\$2 == \\"$ppid\\" { print \\$1 }"`
RETVAL="$pids"
}
# Needed to kill processes running in background
cleanup() {
local current_pid element
local pids=( "$$" )
running_pids=("${pids[@]}")
while :; do
current_pid="${running_pids[0]}"
[ -z "$current_pid" ] && break
running_pids=("${running_pids[@]:1}")
get_pids_of_ppid $current_pid
local new_pids="$RETVAL"
[ -z "$new_pids" ] && continue
for element in $new_pids; do
running_pids+=("$element")
pids=("$element" "${pids[@]}")
done
done
kill ${pids[@]} 2>/dev/null
}
Итак, с самого начала. Предположим, у нас есть терминал, подключенный к / dev / stdout (FD # 1) и / dev / stderr (FD # 2). На практике это может быть труба, розетка или что-то еще.
Результат выполнения скрипта с указанной выше строкой и дополнительно такой:
echo "Will end up in STDOUT(terminal) and /var/log/messages"
...как следует:
$ ./my_script
Will end up in STDOUT(terminal) and /var/log/messages
$ tail -n1 /var/log/messages
Sep 23 15:54:03 wks056 my_script_tag[11644]: Will end up in STDOUT(terminal) and /var/log/messages
Если вы хотите увидеть более четкую картинку, добавьте эти 2 строки в скрипт:
ls -l /proc/self/fd/
ps xf
bash your_script.sh 1>file.log 2>&1
1>file.log
инструктирует оболочку отправлять STDOUT в файл file.log
и 2>&1
указывает перенаправить STDERR (дескриптор файла 2) в STDOUT (дескриптор файла 1).
Примечание: порядок имеет значение, как указано в liw.fi, 2>&1 1>file.log
не работает.
Любопытно, что это работает:
yourcommand &> filename
Но это дает синтаксическую ошибку:
yourcommand &>> filename
syntax error near unexpected token `>'
Вы должны использовать:
yourcommand 1>> filename 2>&1
&>>
Кажется, работает на BASH 4:$ echo $BASH_VERSION 4.1.5(1)-release $ (echo to stdout; echo to stderr > /dev/stderr) &>> /dev/null
Краткий ответ: Command >filename 2>&1
илиCommand &>filename
Объяснение:
Рассмотрим следующий код, который печатает слово «stdout» в stdout, а слово «stderror» - в stderror.
$ (echo "stdout"; echo "stderror" >&2)
stdout
stderror
Обратите внимание, что оператор '&' сообщает bash, что 2 - это дескриптор файла (который указывает на stderr), а не имя файла. Если мы пропустим '&', эта команда напечатает stdout
в stdout, создаст файл с именем "2" и напишетstderror
туда.
Экспериментируя с приведенным выше кодом, вы можете сами убедиться, как работают операторы перенаправления. Например, изменяя, какой файл, какой из двух дескрипторов 1,2
перенаправлен /dev/null
на следующие две строки кода, удаляйте все из stdout и все из stderror соответственно (печатая то, что осталось).
$ (echo "stdout"; echo "stderror" >&2) 1>/dev/null
stderror
$ (echo "stdout"; echo "stderror" >&2) 2>/dev/null
stdout
Теперь мы можем объяснить, почему решение, почему следующий код не производит вывод:
(echo "stdout"; echo "stderror" >&2) >/dev/null 2>&1
Чтобы по-настоящему понять это, я настоятельно рекомендую вам прочитать это веб-страницу в таблицах дескрипторов файлов . Предполагая, что вы сделали это чтение, мы можем продолжить. Обратите внимание, что Bash обрабатывает слева направо; таким образом, Bash видит >/dev/null
первым (что совпадает с 1>/dev/null
) и устанавливает файловый дескриптор 1 так, чтобы он указывал на / dev / null вместо stdout. Сделав это, Bash затем движется вправо и видит 2>&1
. Это устанавливает файловый дескриптор 2 так, чтобы он указывал на тот же файл, что и файловый дескриптор 1 (а не на сам файловый дескриптор 1 !!!! (см. Этот ресурс по указателям).для получения дополнительной информации)) . Поскольку дескриптор файла 1 указывает на / dev / null, а дескриптор файла 2 указывает на тот же файл, что и дескриптор файла 1, дескриптор файла 2 теперь также указывает на / dev / null. Таким образом, оба файловых дескриптора указывают на / dev / null, и поэтому вывод не производится.
Чтобы проверить, действительно ли вы понимаете концепцию, попробуйте угадать вывод, когда мы переключаем порядок перенаправления:
(echo "stdout"; echo "stderror" >&2) 2>&1 >/dev/null
stderror
Причиной здесь является то, что при оценке слева направо Bash видит 2> & 1 и, таким образом, устанавливает дескриптор файла 2 так, чтобы он указывал на то же место, что и дескриптор файла 1, то есть stdout. Затем он устанавливает файловый дескриптор 1 (помните, что> / dev / null = 1> / dev / null), чтобы он указывал на> / dev / null, удаляя, таким образом, все, что обычно отправляется стандарту. Таким образом, все, что нам осталось, это то, что не было отправлено в stdout в подоболочке (код в скобках) - то есть «stderror». Интересно отметить, что хотя 1 является просто указателем на стандартный вывод, перенаправление указателя 2 на 1 через 2>&1
НЕ образует цепочку указателей 2 -> 1 -> стандартный вывод. Если это произошло, в результате перенаправления 1 в / dev / null код2>&1 >/dev/null
выдаст цепочку указателей 2 -> 1 -> / dev / null, и, следовательно, код не сгенерирует ничего, в отличие от того, что мы видели выше.
Наконец, я хотел бы отметить, что есть более простой способ сделать это:
Из раздела 3.6.4 здесь мы видим , что мы можем использовать оператор &>
перенаправления как стандартный вывод и стандартный поток ошибок. Таким образом, чтобы перенаправить вывод stderr и stdout любой команды \dev\null
(которая удаляет вывод), мы просто набираем
$ command &> /dev/null
или в моем примере:
$ (echo "stdout"; echo "stderror" >&2) &>/dev/null
Ключевые выносы:
2>&1 >/dev/null
это! = >/dev/null 2>&1
. Один генерирует вывод, а другой нет!Наконец, взгляните на эти замечательные ресурсы:
Документация Bash по перенаправлению , объяснение таблиц файловых дескрипторов , введение в указатели
LOG_FACILITY="local7.notice"
LOG_TOPIC="my-prog-name"
LOG_TOPIC_OUT="$LOG_TOPIC-out[$$]"
LOG_TOPIC_ERR="$LOG_TOPIC-err[$$]"
exec 3>&1 > >(tee -a /dev/fd/3 | logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_OUT" )
exec 2> >(logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_ERR" )
Это связано: Запись stdOut & stderr в системный журнал.
Это почти работа, но не из xinted; (
Я хотел, чтобы решение получило вывод из stdout плюс stderr в файл журнала, а stderr все еще находился на консоли. Поэтому мне нужно было продублировать вывод stderr через тройник.
Это решение, которое я нашел:
command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile
Для ситуации, когда необходим «трубопровод», вы можете использовать:
| &
Например:
echo -ne "15\n100\n"|sort -c |& tee >sort_result.txt
или
TIMEFORMAT=%R;for i in `seq 1 20` ; do time kubectl get pods |grep node >>js.log ; done |& sort -h
Эти решения на основе bash могут передавать STDOUT и STDERR по отдельности (из STDERR из «sort -c» или из STDERR в «sort -h»).
Следующие функции могут быть использованы для автоматизации процесса переключения выходов между stdout / stderr и файлом журнала.
#!/bin/bash
#set -x
# global vars
OUTPUTS_REDIRECTED="false"
LOGFILE=/dev/stdout
# "private" function used by redirect_outputs_to_logfile()
function save_standard_outputs {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
exit 1;
fi
exec 3>&1
exec 4>&2
trap restore_standard_outputs EXIT
}
# Params: $1 => logfile to write to
function redirect_outputs_to_logfile {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
exit 1;
fi
LOGFILE=$1
if [ -z "$LOGFILE" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"
fi
if [ ! -f $LOGFILE ]; then
touch $LOGFILE
fi
if [ ! -f $LOGFILE ]; then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1
fi
save_standard_outputs
exec 1>>${LOGFILE%.log}.log
exec 2>&1
OUTPUTS_REDIRECTED="true"
}
# "private" function used by save_standard_outputs()
function restore_standard_outputs {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot restore standard outputs because they have NOT been redirected"
exit 1;
fi
exec 1>&- #closes FD 1 (logfile)
exec 2>&- #closes FD 2 (logfile)
exec 2>&4 #restore stderr
exec 1>&3 #restore stdout
OUTPUTS_REDIRECTED="false"
}
Пример использования внутри скрипта:
echo "this goes to stdout"
redirect_outputs_to_logfile /tmp/one.log
echo "this goes to logfile"
restore_standard_outputs
echo "this goes to stdout"
@ Фернанду-fabreti
Добавив к тому, что вы сделали, я немного изменил функции и удалил закрытие & - и это сработало для меня.
function saveStandardOutputs {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
exec 3>&1
exec 4>&2
trap restoreStandardOutputs EXIT
else
echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
exit 1;
fi
}
# Params: $1 => logfile to write to
function redirectOutputsToLogfile {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
LOGFILE=$1
if [ -z "$LOGFILE" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"
fi
if [ ! -f $LOGFILE ]; then
touch $LOGFILE
fi
if [ ! -f $LOGFILE ]; then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1
fi
saveStandardOutputs
exec 1>>${LOGFILE}
exec 2>&1
OUTPUTS_REDIRECTED="true"
else
echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
exit 1;
fi
}
function restoreStandardOutputs {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
exec 1>&3 #restore stdout
exec 2>&4 #restore stderr
OUTPUTS_REDIRECTED="false"
fi
}
LOGFILE_NAME="tmp/one.log"
OUTPUTS_REDIRECTED="false"
echo "this goes to stdout"
redirectOutputsToLogfile $LOGFILE_NAME
echo "this goes to logfile"
echo "${LOGFILE_NAME}"
restoreStandardOutputs
echo "After restore this goes to stdout"
В ситуациях, когда вы рассматриваете возможность использования таких вещей, как exec 2>&1
я, легче читать, если это возможно, переписывать код, используя функции bash, например:
function myfunc(){
[...]
}
myfunc &>mylog.log