Перенаправить stderr и stdout в Bash - PullRequest
612 голосов
/ 12 марта 2009

Я хочу перенаправить как stdout, так и stderr процесса в один файл. Как мне это сделать в Bash?

Ответы [ 15 ]

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

Взгляните здесь . Должно быть:

yourcommand &>filename

(перенаправляет stdout и stderr на имя файла).

423 голосов
/ 12 марта 2009
do_something 2>&1 | tee -a some_file

Это перенаправит stderr на стандартный вывод и стандартный вывод на some_file и распечатает на стандартный вывод.

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

Вы можете перенаправить stderr в стандартный вывод и стандартный вывод в файл:

some_command >file.log 2>&1 

См. http://tldp.org/LDP/abs/html/io-redirection.html

Этот формат предпочтительнее, чем самый популярный &> формат, который работает только в bash. В оболочке Bourne это можно интерпретировать как запуск команды в фоновом режиме. Кроме того, формат более читабелен 2 (STDERR) перенаправлен на 1 (STDOUT).

РЕДАКТИРОВАТЬ: изменил порядок, как указано в комментариях

180 голосов
/ 13 декабря 2013
# 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). На практике это может быть труба, розетка или что-то еще.

  • Создайте FD # 3 и # 4 и укажите на то же «местоположение», что и # 1 и # 2 соответственно. Смена FD # 1 с сегодняшнего дня не влияет на FD # 3. Теперь FDs # 3 и # 4 указывают на STDOUT и STDERR соответственно. Они будут использоваться как real терминал STDOUT и STDERR.
  • 1>> (...) перенаправляет STDOUT на команду в скобках
  • parens (вложенная оболочка) выполняет чтение 'tee' из STDOUT (pipe) exec и перенаправляет команду «logger» через другой канал в вложенную оболочку в parens. Одновременно он копирует тот же вход в FD # 3 (терминал)
  • вторая часть, очень похожая, посвящена тому же трюку для STDERR и FD # 2 и # 4.

Результат запуска скрипта с вышеуказанной строкой и дополнительно такой:

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

Если вы хотите увидеть более четкую картинку, добавьте в скрипт две строки:

ls -l /proc/self/fd/
ps xf
36 голосов
/ 12 марта 2009
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 не работает.

21 голосов
/ 06 мая 2009

Любопытно, что это работает:

yourcommand &> filename

Но это дает синтаксическую ошибку:

yourcommand &>> filename
syntax error near unexpected token `>'

Вы должны использовать:

yourcommand 1>> filename 2>&1
13 голосов
/ 19 ноября 2017

Краткий ответ: Command >filename 2>&1 или Command &>filename


Пояснение:

Рассмотрим следующий код, который печатает слово «stdout» в stdout и слово «stderror» в stderror.

$ (echo "stdout"; echo "stderror" >&2)
stdout
stderror

Обратите внимание, что оператор '&' сообщает bash, что 2 - это дескриптор файла (который указывает на stderr), а не имя файла. Если мы пропустим '&', эта команда выведет 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

Причиной здесь является то, что при оценке слева направо Bash видит 2> & 1 и, таким образом, устанавливает дескриптор файла 2 так, чтобы он указывал на то же место, что и дескриптор файла 1, то есть stdout. Затем он устанавливает файловый дескриптор 1 (помните, что> / dev / null = 1> / dev / null), чтобы он указывал на> / dev / null, удаляя, таким образом, все, что обычно отправляется стандарту. Таким образом, все, что нам осталось, это то, что не было отправлено на стандартный вывод в подоболочке (код в скобках) - то есть "stderror". Интересно отметить, что хотя 1 является просто указателем на стандартный вывод, перенаправление указателя с 2 на 1 через 2>&1 НЕ формирует цепочку указателей 2 -> 1 -> стандартный вывод. Если это произойдет, то в результате перенаправления 1 в / dev / null код 2>&1 >/dev/null даст цепочку указателей 2 -> 1 -> / dev / null, и, следовательно, код ничего не сгенерирует, в отличие от того, что мы видел выше.


Наконец, я бы отметил, что есть более простой способ сделать это:

Из раздела 3.6.4 здесь мы видим, что мы можем использовать оператор &> для перенаправления как stdout, так и stderr. Таким образом, чтобы перенаправить вывод stderr и stdout любой команды на \dev\null (который удаляет вывод), мы просто набираем $ command &> /dev/null или в случае моего примера:

$ (echo "stdout"; echo "stderror" >&2) &>/dev/null

Ключевые выносы:

  • Файловые дескрипторы ведут себя как указатели (хотя файловые дескрипторы не совпадают с файловыми указателями)
  • Переадресация файлового дескриптора "a" на файловый дескриптор "b", который указывает на файл "f", заставляет файловый дескриптор "a" указывать на то же место, что и файловый дескриптор b - файл "f". НЕ образует цепочку указателей a -> b -> f
  • Из-за вышеизложенного порядок имеет значение, 2>&1 >/dev/null равно! = >/dev/null 2>&1. Один генерирует вывод, а другой нет!

Наконец, взгляните на эти замечательные ресурсы:

Документация Bash по перенаправлению , Объяснение таблиц файловых дескрипторов , Введение в указатели

10 голосов
/ 23 апреля 2009
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; (

6 голосов
/ 19 января 2017

Я хотел, чтобы решение получило вывод из stdout плюс stderr в файл журнала, а stderr все еще находился на консоли. Поэтому мне нужно было продублировать вывод stderr через тройник.

Вот решение, которое я нашел:

command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile
  • Первый обмен stderr и stdout
  • затем добавьте стандартный вывод в файл журнала
  • pipe stderr, чтобы соединить и добавить его также в файл журнала
4 голосов
/ 24 октября 2018

Для ситуации, когда необходим "трубопровод", вы можете использовать:

| &

Например:

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").

...