Как перенаправить вывод всей оболочки script внутри самого script?

Можно ли перенаправить все выходные данные оболочки Bourne script куда-нибудь, но с командами оболочки внутри самого script?

Перенаправить вывод одной команды легко, но мне нужно что-то большее:

#!/bin/sh
if [ ! -t 0 ]; then
    # redirect all of my output to a file here
fi

# rest of script...

Значение: если script выполняется неинтерактивно (например, cron), сохраните вывод всего файла в файл. Если вы запускаете интерактивно из оболочки, пусть выход идет на стандартный вывод.

Я хочу сделать это для script, обычно запускаемого периодической утилитой FreeBSD. Это часть ежедневного прогона, который обычно мне не нравится каждый день в электронной почте, поэтому я его не отправил. Однако, если что-то внутри этой конкретной script терпит неудачу, это важно для меня, и я хотел бы иметь возможность собирать и отправлять выходные данные этой части ежедневных заданий.

Обновление: ответ Joshua - спот-на, но я также хотел сохранить и восстановить stdout и stderr вокруг всего script, который выполняется следующим образом:

# save stdout and stderr to file descriptors 3 and 4, then redirect them to "foo"
exec 3>&1 4>&2 >foo 2>&1

# ...

# restore stdout and stderr
exec 1>&3 2>&4

Ответ 1

Решение вопроса как обновлено.

#...part of script without redirection...

{
    #...part of script with redirection...
} > file1 2>file2 # ...and others as appropriate...

#...residue of script without redirection...

Скобки '{...}' обеспечивают единицу перенаправления ввода/вывода. Скобки должны появляться там, где может появиться команда - упрощенно, в начале строки или после точки с запятой. (Да, это может быть уточнено; если вы хотите поспорить, дайте мне знать.)

Вы правы в том, что вы можете сохранить исходные stdout и stderr с указанными вами перенаправлениями, но людям, которым необходимо поддерживать скрипт позже, обычно проще понять, что происходит, если вы расширяете область действия перенаправленного кода, как показано выше.

Соответствующие разделы руководства Bash - это команды группировки и перенаправление ввода/вывода. Соответствующие разделы спецификации оболочки POSIX - это составные команды и перенаправление ввода/вывода. Bash имеет некоторые дополнительные нотации, но в остальном похож на спецификацию оболочки POSIX.

Ответ 2

Обычно мы помещаем один из них в верхнюю часть скрипта или около него. Скрипты, которые анализируют свои командные строки, будут выполнять перенаправление после анализа.

Отправить стандартный вывод в файл

exec > file

с stderr

exec > file                                                                      
exec 2>&1

добавить как stdout, так и stderr в файл

exec >> file
exec 2>&1

Как отметил в своем комментарии Джонатан Леффлер:

exec есть две отдельные работы. Первый - заменить текущую исполняемую оболочку (скрипт) новой программой. Другой - изменение перенаправлений ввода/вывода в текущей оболочке. Это отличается отсутствием аргумента для exec.

Ответ 3

Вы можете сделать всю функцию script такой, как это:

main_function() {
  do_things_here
}

то в конце script выполните следующее:

if [ -z $TERM ]; then
  # if not run via terminal, log everything into a log file
  main_function 2>&1 >> /var/log/my_uber_script.log
else
  # run via terminal, only output to screen
  main_function
fi

В качестве альтернативы вы можете записывать все в файл журнала каждый раз и все равно выводить его на стандартный вывод: просто

# log everything, but also output to stdout
main_function 2>&1 | tee -a /var/log/my_uber_script.log

Ответ 4

Для сохранения исходного stdout и stderr вы можете использовать:

exec [fd number]<&1 
exec [fd number]<&2

Например, следующий код будет печатать "walla1" и "walla2" в файле журнала (a.txt), "walla3" на stdout, "walla4" на stderr.

#!/bin/bash

exec 5<&1
exec 6<&2

exec 1> ~/a.txt 2>&1

echo "walla1"
echo "walla2" >&2
echo "walla3" >&5
echo "walla4" >&6

Ответ 5

[ -t <&0 ] || exec >> test.log