Порядок перенаправления подстановки нескольких процессов Bash

#linux #bash #shell #ubuntu #script

#linux #bash #оболочка #ubuntu #скрипт

Вопрос:

Я пытаюсь использовать несколько замен процессов в команде BASH, но, похоже, я неправильно понимаю порядок, в котором они разрешаются и перенаправляются друг другу.

Система

Версия Ubuntu 18.04
BASH — GNU bash, версия 4.4.20 (1)-релиз (x86_64-pc-linux-gnu)

Проблема

Я пытаюсь перенаправить вывод команды в tee , перенаправить это в ts (добавив временную метку), а затем перенаправить это в split (разделив вывод на отдельные файлы). Я могу перенаправить выходные данные в tee и ts , но при перенаправлении в split я сталкиваюсь с проблемой.

Мои попытки

command >(tee -a >(ts '[%Y-%m-%d %H:%M:%S]' > tempfile.txt)) — это перенаправит вывод в процесс подстановки tee , затем перенаправит на процесс подстановки ts и добавит временную метку, а затем перенаправит на tempfile.txt это то, чего я ожидал бы

command >(tee -a >(ts '[%Y-%m-%d %H:%M:%S]' >(split -d -b 10 -))) — это ничего не дает, хотя я бы надеялся, что результатом была бы куча 10-байтовых файлов с временными метками в разных строках.

Чтобы продолжить тестирование, я попытался echo вместо этого посмотреть, что произойдет command >(tee -a >(ts '[%Y-%m-%d %H:%M:%S]' >(echo))) — печать с начальных tee отпечатков (как и должно быть), но echo печатает пустую строку, по-видимому, это не имеет значения из-за нового результата, который я получил — см. Редактирование внизу

command >(tee -a >(ts '[%Y-%m-%d %H:%M:%S]') >(split -d -b 10 -)) — Это печатает команду с меткой времени (как tee и ts должно быть) и, кроме того, создает 10-байтовые файлы с выводом команды (без метки времени на них). — это то, чего я ожидал, и имеет смысл, поскольку тройник перенаправляется на обе замены процесса отдельно, в основном это была проверка на вменяемость

Что, я думаю, происходит

Из того, что я могу сказать >(ts '[%Y-%m-%d %H:%M:%S]' >(split -d -b 10 -)) >(ts '[%Y-%m-%d %H:%M:%S]' >(echo)) если уж на то пошло), они сначала решаются как самостоятельная полная и отдельная команда. Таким образом split echo ) получают пустой вывод, из ts которого сам по себе не выводится. Только после этого фактическая команда разрешается и отправляет свои выходные данные на замену tee .

Это не объясняет, почему command >(tee -a >(ts '[%Y-%m-%d %H:%M:%S]' > tempfile.txt)) это работает, поскольку по этой теории tee само по себе не имеет выходных данных, поэтому ts должно получать не входные данные, а также должно выводить пробел.

Все это означает, что я не совсем уверен, что происходит. Я думаю, что это связано с порядком разрешения замен процессов и тем, как происходят перенаправления, но для меня ничего не имеет смысла в том, как именно это происходит.

Чего я хочу

По сути, я просто хочу понять, как заставить command >(tee -a >(ts '[%Y-%m-%d %H:%M:%S]' >(split -d -b 10 -))) работать так, как кажется. Мне нужно, чтобы выходные данные команд отправлялись в процесс substitution tee , который отправит его в процесс substitution ts и добавит временные метки, которые будут отправлены, split и разделит вывод на несколько небольших файлов.

Спасибо за ваше время и любую помощь, которую вы можете предоставить.

* Редактировать — я только что попробовал command > >(echo) и увидел, что вывод пустой, что не то, что я ожидал (я ожидал echo получить, а затем вывести вывод команды). Я думаю, что я просто очень не понимаю, как работает замена процесса на данный момент

Комментарии:

1. Почему бы и нет command | ts '[%Y-%m-%d %H:%M:%S]' | split -d -b 10 - ?

2. Я не упоминал об этом, но фактическая команда, которую я использую, разделяет stdout и stderr, и я думаю, что это не позволяет использовать конвейер (но, может быть, я ошибаюсь?). Также я бы предпочел (хотя это скорее «приятно иметь») использовать tee , чтобы мой вывод также отображался на экране одновременно.

3. echo игнорирует его ввод. Возможно, вы думаете о cat , который считывает свои входные данные и записывает все в свои выходные данные.

4. @WilliamPursell спасибо. да, я заметил это, пытаясь поэкспериментировать с cat и увидев, как это работает. Я никогда не понимал, что это так, хотя это echo игнорирует ввод при передаче по каналу.

5. Если вы хотите, чтобы поток ошибок также попадал в канал ts , просто сделайте command 2>amp;1 | ts ... | split ...

Ответ №1:

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

 { { cmd 2>amp;3 | ts ... | split; } 3>amp;1 >amp;4 | ts ... | split; } 4>amp;1
 

Это отправляет выходные cmd данные в первый конвейер, в то время как поток ошибок из cmd поступает во 2-й конвейер. Файловый дескриптор 3 введен для разделения потоков ошибок ts и split разделения, но это может быть нежелательным. fd 4 вводится для split предотвращения потребления выходных данных вторым конвейером, и это может оказаться ненужным ( split например, если не производит никаких выходных данных).

Комментарии:

1. я не уверен на 100%, что это работает с потоком ошибок, потому что я не могу проверить это на данный момент, но в остальном, похоже, работает как шарм! Спасибо! Я просто добавил опцию tee, чтобы она также выводила на экран выходные данные, поэтому в итоге я получил { { cmd 2>amp;3 | ts '[%Y-%m-%d %H:%M:%S]' | tee -a >(split -d -b 10 - out); } 3>amp;1 >amp;4 | ts '[%Y-%m-%d %H:%M:%S]' | tee -a >(split -d -b 10 - e rr); } 4>amp;1

Ответ №2:

Одна вещь, которую вы могли бы сделать, если вы действительно хотите, чтобы одна команда перенаправляла stdin / stderr на отдельный ts|tee|split , это

 command 1> >(ts '[%Y-%m-%d %H:%M:%S]' | tee -a >(split -d -b 10 -)) 2> >(ts '[%Y-%m-%d %H:%M:%S]' | tee -a >(split -d -b 10 -))
 

Но недостатком является то, что tee печатается только после печати приглашения. Вероятно, есть способ избежать этого, дублируя файловые дескрипторы, но это лучшее, что я мог придумать.

Комментарии:

1. … «Все еще ожидаете ввода»? Это зависит от того, что у вас command есть; знаем ли мы, что используемая команда вообще ожидает ввода?

2. Я подозреваю, что вы видите не что-то «ожидающее ввода», а вместо этого отложенные записи, поступающие на консоль после печати приглашения, поэтому приглашение смешивается с выводом — таким образом, нажатие enter просто выводит второе приглашение.

3. @CharlesDuffy ах да, футболка печатается после печати приглашения.

4. … что касается того, как этого можно избежать — я исторически написал код, который используется flock -s для создания общей блокировки для каждой из ваших команд, участвующих в выполнении записи, а затем flock -x для ожидания завершения всех этих процессов (поскольку эксклюзивная блокировка может быть создана только после закрытия всех общих блокировок).

5. (кстати, на самом деле неправильно говорить «tee печатает только после запроса» — tee может и часто будет печатать раньше, в зависимости от того, сколько содержимого есть и сколько времени command требуется для выполнения; просто не гарантируется, что он также не продолжит печатать содержимое command , из которого вышел, и родительскую оболочкуобнаружил это и напечатал приглашение).

Ответ №3:

Это:

 ts '[%Y-%m-%d %H:%M:%S]' >(split -d -b 10 -)
 

расширяет имя файла , сгенерированное подстановкой процесса в командной строке ts , так что запускается что — то вроде ts '[%Y-%m-%d %H:%M:%S]' /dev/fd/63 . ts затем пытается открыть fd то, что переходит split к чтению входных данных оттуда, вместо чтения из исходного stdin.

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

Вы, вероятно, хотели написать

 ts '[%Y-%m-%d %H:%M:%S]' > >(split -d -b 10 -)
                         ^
 

с перенаправлением на процесс подстановки.

Тем не менее, вы могли бы просто использовать канал между ts и split .