Подтвердить что ты не робот

Как заставить STDOUT и STDERR перейти на терминал и файл журнала?

У меня есть script, который будет запускаться интерактивно нетехническими пользователями. script записывает обновления состояния в STDOUT, чтобы пользователь мог убедиться, что script работает нормально.

Я хочу, чтобы оба STDOUT и STDERR перенаправлялись на терминал (чтобы пользователь мог видеть, что работает script, и посмотреть, была ли проблема). Я также хочу, чтобы оба потока были перенаправлены на файл журнала.

Я видел множество решений в сети. Некоторые не работают, а другие ужасно сложны. Я разработал работоспособное решение (которое я введу в качестве ответа), но это kludgy.

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

РЕДАКТИРОВАТЬ: Перенаправление STDERR в STDOUT и соединение результата с типом работы, но это зависит от того, как пользователи запоминают перенаправление и вывод на выход. Я хочу, чтобы журнал был безупречным и автоматическим (вот почему я хотел бы встроить решение в сам script.)

4b9b3361

Ответ 1

Используйте "tee" для перенаправления на файл и на экран. В зависимости от используемой оболочки вы сначала должны перенаправить stderr на stdout, используя

./a.out 2>&1 | tee output

или

./a.out |& tee output

В csh имеется встроенная команда под названием "script", которая будет захватывать все, что идет на экран, в файл. Вы запустите его, набрав "script", затем сделав то, что вы хотите захватить, затем нажмите control-D, чтобы закрыть файл script. Я не знаю эквивалента для sh/bash/ksh.

Кроме того, поскольку вы теперь указали, что это ваши собственные скрипты sh, которые вы можете изменить, вы можете сделать перенаправление внутренне, окружив весь script скобками или скобками, например

  #!/bin/sh
  {
    ... whatever you had in your script before
  } 2>&1 | tee output.file

Ответ 2

Приближается пол-десятилетия спустя...

Я считаю, что это "идеальное решение", которое ищет ОП.

Здесь один вкладыш, который вы можете добавить в начало Bash script:

exec > >(tee -a $HOME/logfile) 2>&1

Здесь небольшой script, демонстрирующий его использование:

#!/usr/bin/env bash

exec > >(tee -a $HOME/logfile) 2>&1

# Test redirection of STDOUT
echo test_stdout

# Test redirection of STDERR
ls test_stderr___this_file_does_not_exist

(Примечание: это работает только с Bash. Работает с с /bin/sh.)

Адаптировано из здесь; оригинал не сделал, из того, что я могу сказать, поймать STDERR в лог файле. Исправлено примечание от здесь.

Ответ 3

для перенаправления stderr на stdout добавьте это в свою команду: 2>&1 Для вывода на терминал и входа в файл вы должны использовать tee

Оба вместе выглядят следующим образом:

 mycommand 2>&1 | tee mylogfile.log

EDIT: для внедрения в ваш script вы сделаете то же самое. Итак, ваш script

#!/bin/sh
whatever1
whatever2
...
whatever3

закончится как

#!/bin/sh
( whatever1
whatever2
...
whatever3 ) 2>&1 | tee mylogfile.log

Ответ 4

Используйте программу tee и dup stderr для стандартного вывода.

 program 2>&1 | tee > logfile

Ответ 5

Я создал script под названием "RunScript.sh". Содержимое этого script:

${APP_HOME}/${1}.sh ${2} ${3} ${4} ${5} ${6} 2>&1 | tee -a ${APP_HOME}/${1}.log

Я называю это следующим образом:

./RunScript.sh ScriptToRun Param1 Param2 Param3 ...

Это работает, но требует, чтобы скрипты приложений выполнялись через внешний script. Это немного клочья.

Ответ 6

Через год, здесь старый bash script для записи чего-либо. Например, фотографии teelog make ... записывается в сгенерированное имя журнала (и см. трюк для входа в вложенный make).

#!/bin/bash
me=teelog
Version="2008-10-9 oct denis-bz"

Help() {
cat <<!

    $me anycommand args ...

logs the output of "anycommand ..." as well as displaying it on the screen,
by running
    anycommand args ... 2>&1 | tee `day`-command-args.log

That is, stdout and stderr go to both the screen, and to a log file.
(The Unix "tee" command is named after "T" pipe fittings, 1 in -> 2 out;
see http://en.wikipedia.org/wiki/Tee_(command) ).

The default log file name is made up from "command" and all the "args":
    $me cmd -opt dir/file  logs to `day`-cmd--opt-file.log .
To log to xx.log instead, either export log=xx.log or
    $me log=xx.log cmd ...
If "logdir" is set, logs are put in that directory, which must exist.
An old xx.log is moved to /tmp/\$USER-xx.log .

The log file has a header like
    # from: command args ...
    # run: date pwd etc.
to show what was run; see "From" in this file.

Called as "Log" (ln -s $me Log), Log anycommand ... logs to a file:
    command args ... > `day`-command-args.log
and tees stderr to both the log file and the terminal -- bash only.

Some commands that prompt for input from the console, such as a password,
don't prompt if they "| tee"; you can only type ahead, carefully.

To log all "make" s, including nested ones like
    cd dir1; \$(MAKE)
    cd dir2; \$(MAKE)
    ...
export MAKE="$me make"

!
  # See also: output logging in screen(1).
    exit 1
}


#-------------------------------------------------------------------------------
# bzutil.sh  denisbz may2008 --

day() {  # 30mar, 3mar
    /bin/date +%e%h  |  tr '[A-Z]' '[a-z]'  |  tr -d ' '
}

edate() {  # 19 May 2008 15:56
    echo `/bin/date "+%e %h %Y %H:%M"`
}

From() {  # header  # from: $*  # run: date pwd ...
    case `uname` in Darwin )
        mac=" mac `sw_vers -productVersion`"
    esac
    cut -c -200 <<!
${comment-#} from: [email protected]
${comment-#} run: `edate`  in $PWD `uname -n` $mac `arch` 

!
    # mac $PWD is pwd -L not -P real
}

    # log name: day-args*.log, change this if you like --
logfilename() {
    log=`day`
    [[ $1 == "sudo" ]]  &&  shift
    for arg
    do
        log="$log-${arg##*/}"  # basename
        (( ${#log} >= 100 ))  &&  break  # max len 100
    done
            # no blanks etc in logfilename please, tr them to "-"
    echo $logdir/` echo "$log".log  |  tr -C '.:+=[:alnum:]_\n' - `
}

#-------------------------------------------------------------------------------
case "$1" in
-v* | --v* )
    echo "$0 version: $Version"
    exit 1 ;;
"" | -* )
    Help
esac

    # scan log= etc --
while [[ $1 == [a-zA-Z_]*=* ]]; do
    export "$1"
    shift
done

: ${logdir=.}
[[ -w $logdir ]] || {
    echo >&2 "error: $me: can't write in logdir $logdir"
    exit 1
    }
: ${log=` logfilename "[email protected]" `}
[[ -f $log ]]  &&
    /bin/mv "$log" "/tmp/$USER-${log##*/}"


case ${0##*/} in  # basename
log | Log )  # both to log, stderr to caller stderr too --
{
    From "[email protected]"
    "[email protected]"
} > $log  2> >(tee /dev/stderr)  # bash only
    # see http://wooledge.org:8000/BashFAQ 47, stderr to a pipe
;;

* )
#-------------------------------------------------------------------------------
{
    From "[email protected]"  # header: from ... date pwd etc.

    "[email protected]"  2>&1  # run the cmd with stderr and stdout both to the log

} | tee $log
    # mac tee buffers stdout ?

esac

Ответ 7

Это помогает, а также сохраняет различие между stdout и stderr:

{ the_cmd > >(tee stdout.txt ); } 2> >(tee stderr.txt >&2 )

Вот скрипт:

the_cmd()
{
    echo out;
    1>&2 echo err;
}

{ the_cmd > >(tee stdout.txt ); } 2> >(tee stderr.txt >&2 )

Здесь сессия:

$ foo=$(./example.sh)
    err

$ echo $foo
    out

$ cat stdout.txt
    out

$ cat stderr.txt
    err

Вот как это работает:

  1. the_cmd отправляется в tee, который сохраняет его в файл, но также передает копию в стандартный вывод своего блока (часть в {фигурных скобках}). stderr из the_cmd по the_cmd делает его блоком stderr.

  2. Мы не перенаправляем стандартный вывод из блока (так как мы уже записали его в файл), поэтому он по умолчанию переносит его в терминал, но мы перенаправляем блок stderr на другую команду tee (поскольку нам все еще нужно захватить его).

  3. Последняя команда tee записывает свой stdin (который является the_cmd) в файл, но также передает копию в stdout, которая смешивает его с stdout блока (мы этого не хотим), поэтому мы явно отправляем его обратно в stderr с >&2.

Это удерживает stderr отдельно от stdout как в файлах, так и в выходных данных команды.

Если первая тройка записывает какие-либо ошибки, они будут отображаться как в файле stderr, так и в команде stderr, если секундная тройка записывает какие-либо ошибки, они будут отображаться только в терминале stderr.

Ответ 8

Используйте команду script в script (man 1 script)

Создайте оболочку shellscript (2 строки), которая устанавливает script(), а затем вызывает exit.

Часть 1: wrap.sh

#!/bin/sh
script -c './realscript.sh'
exit

Часть 2: realscript.sh

#!/bin/sh
echo 'Output'

Результат:

~: sh wrap.sh 
Script started, file is typescript
Output
Script done, file is typescript
~: cat typescript 
Script started on fr. 12. des. 2008 kl. 18.07 +0100
Output

Script done on fr. 12. des. 2008 kl. 18.07 +0100
~: