Принудительно использовать сценарий bash, чтобы использовать тройник без отправки из командной строки


20

У меня есть сценарий bash с большим количеством выходных данных, и я хотел бы изменить этот сценарий (не создавать новый), чтобы скопировать весь вывод в файл, как это было бы, если бы я передавал его через тройник.

У меня есть файл script.sh :

#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT
launch_applications_that_output_to_STDOUT
fini

и я хотел бы сделать копию STDOUT в файл script.log без необходимости ./script.sh | tee script.logкаждый раз печатать .

Спасибо том


Прошу прощения за то, что не сказал этого раньше, но сценарий довольно длинный, поэтому я ищу простое решение. Добавление | ти каждой линии это слишком много.
Том

Ответы:


15

Я не мог заставить работать очень простую однострочную версию Денниса, так что вот гораздо более запутанный метод. Я бы попробовал его первым.

Как уже упоминалось, вы можете использовать exec для перенаправления стандартной ошибки и стандартной ошибки для всего сценария.
exec > $LOGFILE 2>&1 Примерно так: Это выведет все stderr и stdout в $ LOGFILE.

Теперь, так как вы хотите, чтобы это отображалось на консоли, а также в лог-файле, вам также придется использовать именованный канал для exec для записи и tee для чтения.
(Однострочник Денниса технически тоже делает это, хотя, очевидно, по-другому). Сама труба создается с помощью mkfifo $PIPEFILE. Затем сделайте следующее.

# Начните писать в файл журнала, но извлеките его из нашего именованного канала.
тройник $ LOGFILE <$ PIPEFILE &

# захватить идентификатор процесса тройника для команды ожидания.
TEEPID = $!

# перенаправить оставшуюся часть stderr и stdout в наш именованный канал.
exec> $ PIPEFILE 2> & 1

echo "Сделай свои команды здесь"
echo "Все их стандартные выходы получат teed."
echo "Так будет и их стандартная ошибка"> & 2

# закройте дескрипторы файлов stderr и stdout.
exec 1> & - 2> & -

# Дождитесь окончания тройника, так как теперь другой конец трубы закрыт.
подождите $ TEEPID

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

Для справки, я почерпнул большую часть этого из очень информативного поста в блоге случайного парня: ( Архивная версия )


Похоже, это не работает в Cygwin. :-(
что

Этот пост делает хорошую работу по обучению. Большое спасибо.
Фелипе Альварес

27

Просто добавьте это в начало вашего скрипта:

exec > >(tee -ia script.log)

Это добавит все выходные данные, отправленные на стандартный вывод, в файл script.log, оставив прежнее содержимое на месте. Если вы хотите начинать заново каждый раз, когда запускается скрипт, просто добавьте rm script.logпрямо перед этой execкомандой или удалите ее -aиз teeкоманды.

-iОпция заставляет teeигнорировать сигнал прерывания и может позволить , teeчтобы поймать более полный набор выходной.

Добавьте эту строку, чтобы также отлавливать все ошибки (в отдельном файле):

exec 2> >(tee -ia scripterr.out)

Пробелы между несколькими >символами важны.


Очень интересное решение.
ℝaphink

2
Это действительно элегантный способ сделать это по сравнению со всем дерьмом, которое я написал. Но когда я создаю сценарий с этими двумя строками, он зависает и никогда не завершается должным образом.
Кристофер Карел


GNU bash, версия 3.2.25 (1) как часть установки RHEL5.3. Похоже, что это последняя версия из официальных репозиториев.
Кристофер Карел

Я только что попробовал это под cygwin в выпуске Bash 3.2.49 (23) и получил ошибки дескриптора файла. Я работаю в Bash 4.0.33 (1) -релизе в Ubuntu 9.10, так что это может быть вещь Bash 4.
Приостановлено до дальнейшего уведомления.

6

Это комбинированная версия ответа, опубликованного Деннисом Уильямсоном ранее. Добавляет оба err & std в script.log в правильном порядке.

Добавьте эту строку в начало вашего скрипта:

exec > >(tee -a script.log) 2>&1

Обратите внимание на пространство между >знаками. У меня работает на GNU bash, версия 3.2.25 (1) -релиз (x86_64-redhat-linux-gnu)


5

Я полагаю, что другой подход выглядит примерно так:

#!/bin/bash

# start grouping at the top
{ 
    # your script goes here
    do_something_that_outputs_stuff_to_STDOUT
    launch_applications_that_output_to_STDOUT

# and at the bottom, redirect everything from the grouped commands
} 2>&1 | tee script.log 

3

Если вам нужна копия вывода, вы можете использовать teeэтот способ:

  #!/bin/bash
  init
  do_something_that_outputs_stuff_to_STDOUT | tee script.log
  launch_applications_that_output_to_STDOUT | tee -a script.log
  fini

Тем не менее, это будет регистрировать только стандартный вывод в script.log. Если вы хотите убедиться, что и stderr, и stdout перенаправлены, используйте:

  #!/bin/bash
  init
  do_something_that_outputs_stuff_to_STDOUT 2>&1 | tee script.log
  launch_applications_that_output_to_STDOUT 2>&1 | tee -a script.log
  fini

Вы могли бы даже сделать это немного лучше с небольшой функцией:

  #!/bin/bash

  LOGFILE="script.log"
  # Wipe LOGFILE if you don't want to add to it at each run
  rm -f "$LOGFILE"

  function logcmd() {
        local cmd="$1" logfile=${LOGFILE:-script.log} # Use default value if unset earlier

        # Launch command, redirect stderr to stdout and append to $logfile
        eval '$cmd' 2>&1 | tee -a "$logfile"
  }

  init
  logcmd "do_something_that_outputs_stuff_to_STDOUT"
  logcmd "launch_applications_that_output_to_STDOUT"
  fini

Хороший ответ. Если бы он действительно заботился только о stdout, я бы просто ушел 2>&1и просто отправил трубку в тройник.
DaveParillo

Это правда, я включил только stderr, потому что предыдущий ответ от Игоря хотел перенаправить его, и я подумал, что это хорошая идея.
ℝaphink

1

Вы бы просто использовали это:

script logfile.txt
your script or command here

Это выводит все в этот журнал, все, а также отображает его на экране. Если вы хотите ПОЛНЫЙ вывод, это как это сделать.

И затем вы можете повторить сценарий, если вы ленивы.


Хорошо бы отметить, что scriptэто пакет, например. sudo apt-get install scriptчтобы установить его на дистрибутивы со вкусом Debian , дополнительно script -ac 'command opts' ~/Documents/some_log.scriptможно просмотреть в терминале что-то вроде strings ~/Documents/some_log.script | more; stringsбыть простым способом предварительной дезинфекции некоторых вещей, которые script вводят, чтобы позволить более изощренные методы повторного воспроизведения / просмотра. В противном случае твердый ответ @Phishy, ​​потому что scriptтакже сохраняет интерактивные вещи тоже.
S0AndS0

0
#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT > script.log 2>&1
launch_applications_that_output_to_STDOUT >> script.log 2>&1
fini

Вы можете узнать больше о том, как это работает, здесь: http://www.xaprb.com/blog/2006/06/06/what-does-devnull-21-mean/


1
Я добавил второе> во вторую строку в ответе Игоря, чтобы оно не уничтожило журнал, записанный в первой строке.
Даг Харрис

1
Хотя он хочет копию, поэтому ему нужно использовать tee.
ℝaphink

@Raphink: Это верно :)
Том
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.