Вопрос

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

Это было полезно?

Решение

Посмотрите здесь . Должно быть:

yourcommand &>filename

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

Другие советы

do_something 2>&1 | tee -a some_file

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

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

some_command >file.log 2>&1 

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

Этот формат предпочтительнее, чем самый популярный & amp; > формат, который работает только в 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'"

Теперь простое echo запишет данные в $LOG_FILE.Полезно для демонизации.

Автору оригинального поста,

Это зависит от того, чего вам нужно достичь.Если вам просто нужно перенаправить ввод / вывод команды, которую вы вызываете из своего скрипта, ответы уже даны.Мой - о перенаправлении внутри текущий скрипт, который влияет на все команды / встроенные модули (включая форки) после упомянутого фрагмента кода.


Еще одно классное решение заключается в перенаправлении как на std-err / out, так И на logger или log file одновременно, что включает в себя разделение "потока" на два.Эта функциональность обеспечивается командой '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).На практике это может быть труба, розетка или что-то еще.

  • Создайте FDS #3 и # 4 и укажите на то же "местоположение", что и #1 и # 2 соответственно.Изменение FD # 1 отныне не влияет на FD # 3.Теперь FDS #3 и #4 указывают на STDOUT и STDERR соответственно.Они будут использоваться в качестве реальный стандартный вывод терминала и стандартный ввод.
  • 1> > (...) перенаправляет стандартный вывод на команду в скобках
  • parens (вспомогательная оболочка) выполняет чтение 'tee' из стандартного вывода exec (канала) и перенаправляет команду 'logger' через другой канал на вспомогательную оболочку в parens.В то же время он копирует тот же вход в FD # 3 (терминал).
  • вторая часть, очень похожая, посвящена выполнению того же трюка для STDERR и FDS #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

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

ls -l /proc/self/fd/
ps xf
bash your_script.sh 1>file.log 2>&1

1> file.log указывает оболочке отправлять STDOUT в файл file.log , а 2 > & amp; 1 сообщает об этом перенаправить STDERR (дескриптор файла 2) в STDOUT (дескриптор файла 1).

Примечание. Порядок имеет значение, как указано в liw.fi, 2 > & 1; > file.log не работает.

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

yourcommand &> filename

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

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

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

yourcommand 1>> filename 2>&1

Краткий ответ: 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 следующие две строки кода удаляют все из стандартного вывода и все из 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 вместо стандартного вывода.Сделав это, 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, т.е. стандартный вывод.Затем он устанавливает файловый дескриптор 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 по перенаправлению, Объяснение таблиц файловых дескрипторов, Введение в указатели

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 & amp; stderr в системный журнал.

Это почти работает, но не из xinted; (

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

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

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

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

|&

Например:

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

"Самый простой" способ (только для bash4): ls * 2>&- 1>&-.

Следующие функции могут использоваться для автоматизации процесса переключения выходов между 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"
    }

Для tcsh я должен использовать следующую команду:

command >& file

Если используется команда & > файл , он выдаст " Неверную нулевую команду " ошибка.

@фернандо-фабрети

В дополнение к тому, что вы сделали, я немного изменил функции и убрал &- closing, и у меня это сработало.

    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 > & amp; 1 , мне легче читать, если это возможно, переписывание кода с использованием функций bash, таких как:

function myfunc(){
  [...]
}

myfunc &>mylog.log
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top