Примушуйте скрипт bash використовувати tee без піппінгу з командного рядка


20

У мене є скрипт bash з великою кількістю результатів, і я хотів би змінити цей скрипт (а не створити новий), щоб скопіювати весь вихід у файл, як би це сталося, якби я пропускав його через tee.

У мене є файл 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. Потім виконайте наступне.

# Почніть писати трійку в лог-файл, але витягніть його з назви.
tee $ LOGFILE <$ PIPEFILE &

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

# переспрямовуємо решту stderr та stdout до нашої названої труби.
exec> $ PIPEFILE 2> & 1

ехо "Зробіть свої команди тут"
відлуння "Усі їхні стандартні норми дістануться".
echo "Так буде їх стандартна помилка"> & 2

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

# Зачекайте, коли трійник закінчиться з тих пір, коли інший кінець труби закрився.
зачекайте $ TEEPID

Якщо ви хочете бути ретельними, ви можете створити та знищити названий файл-файл на початку та в кінці сценарію.

Для запису я зібрав більшу частину цього з дуже інформативної публікації в блозі випадкового хлопця: ( Архівна версія )


Схоже, це не працює у cygwin. :-(
док.

Ця посада добре справляється з навчанням. Велике спасибі.
Феліпе Альварес

27

Просто додайте це до початку вашого сценарію:

exec > >(tee -ia script.log)

Це додасть весь вихідний файл script.log, відправлений до stdout у файл , залишивши попередній вміст на місці. Якщо ви хочете почати свіжий кожен раз , коли скрипт запускається просто додати rm script.logперед цією execкомандою або видалити -aз teeкоманди.

Цей -iпараметр викликає teeігнорування сигналів переривання і може дати teeможливість отримати більш повний набір вихідних даних.

Додайте цей рядок, щоб також знайти всі помилки (в окремому файлі):

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

Пробіли між декількома >символами є важливими.


Дуже цікаве рішення.
phaphink

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

Це комбінована версія відповіді, опублікована Деннісом Вільямсоном раніше. Додає обидві помилки та std до script.log у правильному порядку.

Додайте цей рядок до початку сценарію:

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

Відзначте пробіл між >знаками. Для мене працює в GNU bash, версія 3.2.25 (1) -release (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

Це буде входити тільки stdout в 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, тому що попередня відповідь Ігоря стосувалася перенаправлення, і я вважав, що це гарна ідея.
phaphink

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.
phaphink

@Raphink: Саме так :)
Том
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.