Принудительно использовать сценарий 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
Это действительно элегантный способ сделать это по сравнению со всем дерьмом, которое я написал. Но когда я создаю сценарий с этими двумя строками, он зависает и никогда не завершается должным образом.
Кристофер Карел
Какая версия Bash?
Приостановлено до дальнейшего уведомления.
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
ℝaphink
источник
Хороший ответ. Если бы он действительно заботился только о stdout, я бы просто ушел 2>&1и просто отправил трубку в тройник.
DaveParillo
Это правда, я включил только stderr, потому что предыдущий ответ от Игоря хотел перенаправить его, и я подумал, что это хорошая идея.
ℝaphink
1

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

script logfile.txt
your script or command here

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

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

Phishy
источник
Хорошо бы отметить, что 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: Это верно :)
Том