Запись результатов в файл журнала и консоль

99

В оболочке Unix у меня есть файл env (файл env определяет параметры, необходимые для запуска пользовательского скрипта, такие как имя файла журнала и путь, перенаправление выходных данных и ошибок в файл журнала, сведения о подключении к базе данных и т. Д. ), Который перенаправляет все выходы ( эхо-сообщения ) и ошибки в файл журнала из выполненного скрипта, используя следующий код:

exec 1>>${LOG_FILE}
exec 2>>${LOG_FILE}

Файл env выполняется в начале каждого скрипта. Из-за приведенного выше кода в файле env все выходы консоли, которые могут быть пользовательскими выходами или ошибками, напрямую выводятся в файл журнала, что мне действительно нужно.

Но есть некоторые выборочные пользовательские выходные данные, которые я хочу отображать как в консоли, так и в файле журнала. Но из-за приведенного выше кода я не могу этого сделать.

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

Есть ли способ получить вывод как в консоли, так и в файле журнала, не удаляя приведенные выше коды?

Abinash Shrestha
источник

Ответы:

106
exec 3>&1 1>>${LOG_FILE} 2>&1

отправит вывод stdout и stderr в файл журнала, но также оставит вас с fd 3, подключенным к консоли, так что вы можете сделать

echo "Some console message" 1>&3

написать сообщение прямо в консоль, или

echo "Some console and log file message" | tee /dev/fd/3

для записи сообщения как в консоль, так и в файл журнала - teeотправляет свой вывод как на собственный fd 1 (который здесь LOG_FILE), так и в файл, в который вы сказали ему писать (здесь fd 3, то есть консоль).

Пример:

exec 3>&1 1>>${LOG_FILE} 2>&1

echo "This is stdout"
echo "This is stderr" 1>&2
echo "This is the console (fd 3)" 1>&3
echo "This is both the log and the console" | tee /dev/fd/3

напечатал бы

This is the console (fd 3)
This is both the log and the console

на консоли и поставить

This is stdout
This is stderr
This is both the log and the console

в файл журнала.

Ян Робертс
источник
2
Это сработало именно так, как вы предложили. Но я не понял tee / dev / fd / 3 . Я знаю, что tee записывает сообщение в файл журнала и консоль, но я не совсем понял,
какой
@shrestha это необычное использование тройника, я согласен. Это /dev/fd/3имя файла, которое относится к «текущему открытому fd 3», поэтому tee /dev/fd/3все, что приходит на его stdin , будет записываться в fd 1, а также на fd 3 ( /dev/fd/3файл). Fd 1 подключен к файлу журнала, fd 3 подключен к консоли.
Ян Робертс
Также вы хотите записывать только в файл, а не в консоль, попробуйте: echo "blah" | tee file1.txt | tee file2.txt >/dev/null «Blah» не будет помещаться в file1.txt и file2.txt, но не записывается в консоль.
dangerous89 06
Это мне очень помогло. Хотя я заметил кое-что, что может быть не по теме, но, возможно, некоторые из вас знают причины этого. Я выполняю сценарии R из сценария bash. Консольный вывод различных функций R окрашен (как я его определил). при использовании вышеупомянутого подхода для перенаправления вывода в консоль И файл журнала вывод консоли больше не окрашивается. В чем может быть причина?
dieHellste
1
@dieHellste некоторые программы могут определять, когда их вывод передается по конвейеру другому процессу (в данном случае tee, который, в свою очередь, пишет на терминал), а не напрямую на терминал, и корректировать свой вывод для соответствия.
Ян Робертс
43

Да, вы хотите использовать tee:

tee - читать со стандартного ввода и записывать в стандартный вывод и файлы

Просто передайте свою команду tee и передайте файл в качестве аргумента, например:

exec 1 | tee ${LOG_FILE}
exec 2 | tee ${LOG_FILE}

При этом вывод выводится в STDOUT и тот же вывод записывается в файл журнала. См. man teeДополнительную информацию.

Обратите внимание, что это не будет записывать stderr в файл журнала, поэтому, если вы хотите объединить два потока, используйте:

exec 1 2>&1 | tee ${LOG_FILE}
Джон Кэрнс
источник
2
Вышеупомянутое решение не сработало. У меня есть файл redirect.env как: ##### redirect.env ###### export LOG_FILE = log.txt exec 1 2> & 1 | tee -a $ {LOG_FILE} exec 1 | tee -a $ {LOG_FILE} exec 2 | tee -a $ {LOG_FILE} ######### и рабочий файл содержал следующий код: ##### output.sh ##### #! / bin / sh. redirect.env echo "Допустимый вывод" ech "недопустимый вывод" ##############, но я получаю сообщение об ошибке: #### redirect.env: строка 3: exec: 1: не найдено redirect.env: строка 5: exec: 1: не найдена redirect.env: строка 6: exec: 2: not found #### и в файле журнала я также получаю ту же ошибку. Я что-то не так делаю?
abinash shrestha
Это очень сложно сказать, поскольку в вашем комментарии убраны новые строки. Не могли бы вы добавить вставку кода где-нибудь вроде сути ?
Джон Кэрнс,
1
Я добавил файлы по ссылке UnixRedirect . Связанные файлы: redirect.env и output.sh
abinash shrestha
1
Этот код, похоже, не работает (по крайней мере, больше). Обычно вы получаетеscript.sh: line 5: exec: 1: not found
tftd
39

Я попробовал ответить Джунти, но я также получил

exec: 1: не найдено

ошибка. Это то, что лучше всего работает для меня ( подтверждено также, что работает в zsh):

#!/bin/bash
LOG_FILE=/tmp/both.log
exec > >(tee ${LOG_FILE}) 2>&1
echo "this is stdout"
chmmm 77 /makeError

Затем файл /tmp/both.log содержит

this is stdout
chmmm command not found 

/Tmp/both.log добавляется, если вы не удалите -a из tee.

Подсказка: >(...)это подмена процесса. Это позволяет execв teeкоманде , как если бы это был файл.

Альфонкс
источник
1
Для меня это сработало как шарм, в то время как другие ответы были неудачными.
Джей Тейлор,
Спасибо, что поделились этим фрагментом. Кажется, это работает нормально (по сравнению с другими ответами)!
tftd 06
Это работает, но теперь моя оболочка после выполнения изменилась.
Джош Усре
@JoshUsre Если вам нужна помощь или вы хотите помочь другим SO-пользователям, пожалуйста, объясните, в чем отличие, и опишите вашу оболочку, версию, ОС и т. Д.
alfonx
1
Если вам не нужно различать stdout и stderr, вы можете объединить операторы в exec > >(tee ${LOG_FILE}) 2>&1.
darkdragon
5

Я хотел отображать журналы на stdout и файл журнала вместе с меткой времени. Ни один из приведенных выше ответов не помог мне. Я использовал подстановку процесса и команду exec и придумал следующий код. Примеры журналов:

2017-06-21 11:16:41+05:30 Fetching information about files in the directory...

Добавьте следующие строки вверху вашего скрипта:

LOG_FILE=script.log
exec > >(while read -r line; do printf '%s %s\n' "$(date --rfc-3339=seconds)" "$line" | tee -a $LOG_FILE; done)
exec 2> >(while read -r line; do printf '%s %s\n' "$(date --rfc-3339=seconds)" "$line" | tee -a $LOG_FILE; done >&2)

Надеюсь, это кому-то поможет!

Джиоти Дхиман
источник
Можно ли регистрировать все ошибки приложения для входа в каталог хоста, чтобы это помогало DevOps ....
Прасад Шинде
3

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

# declaring variables

Logfile="logfile.txt"   
MAIL_LOG="Message to print in log file"  
Location="were is u want to store log file"

cd $Location   
if [ -f $Logfile ]  
then   
echo "$MAIL_LOG " >> $Logfile

else        

touch $Logfile   
echo "$MAIL_LOG" >> $Logfile    

fi  

output: 2. Файл журнала будет создан при первом запуске и будет обновляться при следующих запусках. Если файл журнала отсутствует при следующем запуске, сценарий создаст новый файл журнала.

user2197712
источник
1

Я нашел способ получить желаемый результат. Хотя это может быть несколько неортодоксальный способ. В любом случае, вот оно. В файле redir.env у меня есть следующий код:

#####redir.env#####    
export LOG_FILE=log.txt

      exec 2>>${LOG_FILE}

    function log {
     echo "$1">>${LOG_FILE}
    }

    function message {
     echo "$1"
     echo "$1">>${LOG_FILE}
    }

Затем в реальном сценарии у меня есть следующие коды:

#!/bin/sh 
. redir.env
echo "Echoed to console only"
log "Written to log file only"
message "To console and log"
echo "This is stderr. Written to log file only" 1>&2

Здесь эхо выходы только на консоль, журнал выводит только файл журнала и сообщение результатов как для файла журнала и консоли.

После выполнения вышеуказанного файла сценария я получаю следующие результаты:

В консоли

В консоли
Отображается только на консоль
В консоль и в журнал

Для файла журнала

В файле журнала Записывается только в файл журнала
Это stderr. Записывается только в файл журнала
В консоль и в журнал

Надеюсь на эту помощь.

Abinash Shrestha
источник
1

Попробуйте это, он сделает свою работу:

log_file=$curr_dir/log_file.txt
exec > >(tee -a ${log_file} )
exec 2> >(tee -a ${log_file} >&2)
Amousa
источник
Это exec > >(tee -a ${log_file} )идеально подходит для моих нужд. Предыдущие решения, приведенные выше, прерывали бы части моего скрипта, которые принудительно завершали работу в случае сбоя. Спасибо
MitchellK
1
    #
    #------------------------------------------------------------------------------
    # echo pass params and print them to a log file and terminal
    # with timestamp and $host_name and $0 PID
    # usage:
    # doLog "INFO some info message"
    # doLog "DEBUG some debug message"
    # doLog "WARN some warning message"
    # doLog "ERROR some really ERROR message"
    # doLog "FATAL some really fatal message"
    #------------------------------------------------------------------------------
    doLog(){
        type_of_msg=$(echo $*|cut -d" " -f1)
        msg=$(echo "$*"|cut -d" " -f2-)
        [[ $type_of_msg == DEBUG ]] && [[ $do_print_debug_msgs -ne 1 ]] && return
        [[ $type_of_msg == INFO ]] && type_of_msg="INFO " # one space for aligning
        [[ $type_of_msg == WARN ]] && type_of_msg="WARN " # as well

        # print to the terminal if we have one
        test -t 1 && echo " [$type_of_msg] `date "+%Y.%m.%d-%H:%M:%S %Z"` [$run_unit][@$host_name] [$$] ""$msg"

        # define default log file none specified in cnf file
        test -z $log_file && \
            mkdir -p $product_instance_dir/dat/log/bash && \
                log_file="$product_instance_dir/dat/log/bash/$run_unit.`date "+%Y%m"`.log"
        echo " [$type_of_msg] `date "+%Y.%m.%d-%H:%M:%S %Z"` [$run_unit][@$host_name] [$$] ""$msg" >> $log_file
    }
    #eof func doLog
Йордан Георгиев
источник
0

Я считаю очень полезным добавлять в файл журнала и stdout, и stderr. Я был рад увидеть решение от alfonx с exec > >(tee -a), потому что мне было интересно, как это сделать, используя exec. Я наткнулся на творческое решение с использованием синтаксиса here-doc и .: /unix/80707/how-to-output-text-to-both-screen-and-file-inside-a-shell -скрипт

Я обнаружил, что в zsh решение here-doc можно изменить с помощью конструкции multios для копирования вывода как в stdout / stderr, так и в файл журнала:

#!/bin/zsh
LOG=$0.log
# 8 is an arbitrary number;
# multiple redirects for the same file descriptor 
# triggers "multios"
. 8<<\EOF /dev/fd/8 2>&2 >&1 2>>$LOG >>$LOG
# some commands
date >&2
set -x
echo hi
echo bye
EOF
echo not logged

Он не так удобочитаем, как execрешение, но имеет то преимущество, что позволяет вам регистрировать только часть скрипта. Конечно, если вы опустите EOF, тогда весь скрипт будет выполняться с логированием. Я не уверен, как zshреализует multios, но у него может быть меньше накладных расходов, чем у tee. К сожалению, кажется, что нельзя использовать multios с exec.

Метаморфический
источник