Как сохранить stdout в один файл, stderr в другой файл, stdout+stderr в третий файл, а также вывести stdout + stderr на терминал, как обычно для скрипта оболочки?
Я нашел это в другом месте:
exec > >(tee std_out) 2> >(tee err_out >&2)
ls # Should got to std_out
fsdfs # Command not found goes to err_out
Что очень близко. Если я запускаю, bash test.sh 2>&1 | tee output
то это работает, но у меня нет доступа к тому, как выполняется мой скрипт. Это система cicd. Мне нужно иметь возможность делать "комбинированный вывод" изнутри скрипта с помощью exec.
Я создаю библиотеку CI/CD и не могу знать, для чего клиенты будут ее использовать, поэтому я хочу учесть каждый вариант использования.
решение1
Просто расширенный вариант вашего подхода:
exec 2> >(tee -a stderr stdall) 1> >(tee -a stdout stdall)
Стандартная ошибка будет записана в файл с именем stderr
, стандартный вывод — в stdout
, а стандартная ошибка и стандартный вывод также будут записаны в консоль (или на то, на что указывают два файловых дескриптора во время exec
запуска) и в stdall
.
tee -a
(добавить) требуется для предотвращения stdall
перезаписи вторым файлом tee
, который начнет запись в него.
Обратите внимание, чтоприказв котором выполняются перенаправления, имеет значение: вторая подстановка процесса затронута первым перенаправлением, т. е. ошибки, которые она выдала, будут отправлены в >(tee -a stderr stdall)
. Конечно, вы можете перенаправить стандартную ошибку подстановки второго процесса в , чтобы /dev/null
избежать этого побочного эффекта. Перенаправление стандартного вывода перед стандартной ошибкой отправит каждую ошибку в stdout
и stdall
тоже.
Так как команды в процессе Bash выполняются подстановкиасинхронно, нет способа гарантировать, что их вывод будет отображаться в том порядке, в котором он был сгенерирован. Хуже того, фрагменты из стандартного вывода и стандартной ошибки, скорее всего, окажутся на одной строке.
решение2
Ваш скрипт может выполняться самостоятельно $0
(с установкой и проверкой переменной окружения, чтобы избежать бесконечной рекурсии) вместо того, чтобы полагаться на > >(...)
конструкцию bash, которая IMLE капризна и ненадежна.
if [ "$REDIRECTED" != 1 ]; then
export REDIRECTED=1
set -o pipefail
{ { "$0" | tee stdout >&3; } 2>&1 | tee stderr; } 3>&1 | tee stdboth
exit
fi
# rest of your script here
Так как tee
не использует буферизацию строк (и не может быть принудительно сделана с помощью stdbuf(1)
), порядок данных, записанных в stdout и stderr, не будет соблюден в конечном выводе. С командой, которая использует полную буферизацию и пишет как в stdout, так и в stderr, даже буферизация строк tee
не поможет и, что еще хуже, вы можете получить в выводе строки, которые наполовину являются stdout и наполовину stderr.
Я не думаю, что эту проблему можно решить, используя только язык оболочки и легкодоступные утилиты командной строки.
решение3
Я создаю библиотеку CI/CD и не могу знать, для чего клиенты будут ее использовать, поэтому я хочу учесть каждый вариант использования.
Я сомневаюсь в необходимости bash для обработки выходных данных, учитывая, что это сценарий. В идеале в этом контексте вы хотели бы ставить временную метку выходным данным и присваивать им идентификатор для стандартного типа выходных данных, а приложение должно решать, что делать с сообщениями.