Каково значение и использование 2>&1 | tee /dev/stderr

Вопрос или проблема

для следующей команды
output=$(cat $file | docker exec -i CONTAINER COMMAND 2>&1 | tee /dev/stderr)

какое значение и использование
2>&1 | tee /dev/stderr? Я поискал в Google и обнаружил, что 2>&1 означает “объединить stderr и stdout в поток stdout.” tee означает “читает из стандартного ввода и одновременно записывает в стандартный вывод и в один или несколько файлов.” Но я не могу понять, если объединить
2>&1 | tee /dev/stderr

это объединяет stderr и stdout в поток stdout, но затем снова записывает его в /dev/stderr (&2)?

зачем объединять в stdout, а затем перенаправлять обратно в stderr?

и после записи в /dev/stderr, будет ли сообщение сохранено в переменной output в

output=$(cat $file | docker exec -i CONTAINER COMMAND 2>&1 | tee /dev/stderr)

?

Это попытка отправить вывод и ошибки docker как в stderr, так и сохранить их в переменной $output, но этот нецитируемый $file, отсутствие — (и возможно UUOC), это использование /dev/stderr (по крайней мере на Linux или Cygwin) все неверны. Статус выхода docker также теряется. Чтобы сделать это правильно, лучше перейти на zsh и выполнить:
output=$(<$file docker exec -i CONTAINER COMMAND >&1 >&2 2>&1)

В zsh, когда файловый дескриптор (здесь 1) переадресуется более одного раза для вывода, он вместо этого переадресуется в канал к внутреннему процессу, подобному tee. Проходя через эти переадресации, мы имеем:

>&1 (коротко для 1>&1) ничего не делает, но там, чтобы мы могли иметь более одной переадресации файлового дескриптора 1. На этом этапе файловый дескриптор 1 все еще указывает на канал, созданный заменой команды, которая используется для заполнения $output.
>&2 (коротко для 1>&2). Это вторая переадресация файлового дескриптора 1. Поскольку файловый дескриптор 1 был уже перенаправлен ранее (выше), он становится пишущим концом канала. А на другом конце этого канала, у нас есть этот внутренний процесс, подобный tee, который распределяет то, что он читает из этого канала, на то, что было изначально на файловом дескрипторе 1 (канал к $output) и на то, что открыто на stderr¹
2>&1, файловый дескриптор 2 идет в тот же канал, что и выше.

Подозреваю, что намерение автора могло быть таковым: чтобы вывод и ошибки docker шли туда, куда они должны были идти, и в то же время сохранялись оба в $output, в таком случае, это выглядело бы так (все еще в zsh):
3>&1 output=$(
<$file docker exec -i CONTAINER COMMAND 2>&2 2>&1 >&1 >&3 3>&-
)

Где и файловый дескриптор 1, и 2 в конечном итоге отправляются отдельно через tee:

2 в оригинальный stderr и в $output
1 в $output и в оригинальный stdout, который мы делаем доступным внутри подстановки команды, используя этот файловый дескриптор 3.

¹ это может быть канал, терминальное устройство, если выполняется из терминала, это может быть сокет (как это обычно бывает для процессов, запускаемых systemd, где stderr обычно является сокетом Unix domain, подключенным к journald), обычный файл, открытый в режиме дополнения или нет. В Linux, использование tee /dev/stderr, когда stderr открыт на сокете, приведет к ошибке, так как нельзя открыть сокет с помощью open(), а для обычного файла (например, лог-файла) он обнулит его, потеряв все его предыдущие содержимое!

Значение:
2>&1 перенаправляет STDERR в STDOUT
| перенаправляет STDOUT предыдущей команды в STDIN следующей команды
tee /dev/stderr получает STDIN и отправляет его в STDERR (устройство /dev/stderr) и в STDOUT
В итоге: объединяет STDERR и STDOUT и отправляет объединенный поток в STDERR и STDOUT.

Ответ или решение

Теория

Команда 2>&1 | tee /dev/stderr является общепринятым методом управления потоками ввода-вывода в Unix-подобных системах и используется для одновременного перенаправления стандартного вывода (STDOUT) и стандартного вывода ошибок (STDERR). Чтобы полностью понять её суть, давайте разберём каждый элемент этой команды.

  1. 2>&1: Это перенаправление STDERR в STDOUT. В Unix-подобных системах потоки ввода/вывода представляются через файловые дескрипторы: 0 — стандартный ввод (STDIN), 1 — стандартный вывод (STDOUT), 2 — стандартный вывод ошибок (STDERR). Выражение 2>&1 говорит системе о том, что STDERR должен быть перенаправлен на ту же "дорогу", что и STDOUT, т.е. ошибки будут выводиться в тот же поток, что и стандартный вывод.

  2. |: Это конвейер, который берёт STDOUT предыдущей команды и направляет его на STDIN следующей. Это один из столпов командной линии Unix, позволяющий создавать сложные цепочки команд.

  3. tee /dev/stderr: Команда tee читает из стандартного ввода (который здесь поступает из предыдущего конвейера) и записывает одновременно в стандартный вывод и в файл или устройство, указанное как аргумент. В данном случае, /dev/stderr — это специальное устройство в Unix-системах, обращение к которому позволяет писать в стандартный вывод ошибок текущего процесса.

Пример

Рассмотрим пример использования команды:

output=$(cat $file | docker exec -i CONTAINER COMMAND 2>&1 | tee /dev/stderr)

В этом случае команда последовательно выполняется следующим образом:

  1. cat $file — считывает содержимое файла и передаёт его в STDIN для следующей команды.
  2. docker exec -i CONTAINER COMMAND — выполняет указанный COMMAND внутри контейнера Docker, получая STDIN из предыдущего cat.
  3. 2>&1 — перенаправляет STDERR этой команды в STDOUT.
  4. | tee /dev/stderr — берёт объединённый поток STDOUT и STDERR, переданный из docker exec, и направляет его одновременно в STDERR и STDOUT.

Применение

Цель такого конвейера — одновременно отображать вывод команды в реальном времени на стандартное устройство ошибок и одновременно сохранять этот вывод в переменной. Это может быть полезно в нескольких сценариях:

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

  • Логирование: сохранение данных в переменной output позволяет затем анализировать или обрабатывать вывод программmatically, без необходимости повторного выполнения команды.

Заключение

Итак, основная цель этой техники заключается в одновременном захвате вывода в переменную и его транспарантного отображения на устройстве вывода ошибок. Это делает процесс более гибким и позволяет решать сразу несколько задач: отладка, логирование и анализ данных в одном потоке выполнения. Использование таких приемов в Unix-подобных системах позволяет эффективно управлять потоками данных и улучшать качество написания скриптов для администраторов и разработчиков.

Оцените материал
Добавить комментарий

Капча загружается...