Я изучил почти все доступные подобные вопросы , но безрезультатно.
Позвольте мне подробно описать проблему:
Я запускаю несколько автоматических сценариев, и они могут генерировать стандартный вывод и стандартные строки ошибок, я хочу записать их в точном порядке, отображаемом эмулятором терминала, а затем добавить к ним префикс, такой как «STDERR:» и «STDOUT:».
Я пытался использовать трубы и даже основанный на эполле подход к ним, но безрезультатно. Я думаю, что решение находится в pty использовании, хотя я не мастер в этом. Я также заглянул в исходный код VTE Gnome , но это не было очень продуктивным.
В идеале я бы использовал Go вместо Bash для этого, но я не смог. Похоже, что трубы автоматически запрещают поддерживать правильный порядок строк из-за буферизации.
Кто-нибудь смог сделать что-то подобное? Или это просто невозможно? Я думаю, что если эмулятор терминала может это сделать, то это не так - может быть, путем создания небольшой программы на C, которая по-разному обрабатывает PTY?
В идеале я хотел бы использовать асинхронный ввод для чтения этих двух потоков (STDOUT и STDERR), а затем перепечатывать их, как мне нужно, но порядок ввода имеет решающее значение!
ПРИМЕЧАНИЕ: я знаю о stderred, но он не работает для меня со сценариями Bash и не может быть легко отредактирован для добавления префикса (поскольку он в основном содержит множество системных вызовов).
Обновление: добавлено ниже двух гист
- Пример программы, которая генерирует смешанный stdout / stderr
- Ожидаемый результат от программы выше
(случайные задержки в секунду могут быть добавлены в пример сценария, который я предоставил, чтобы доказать непротиворечивый результат)
Обновление: решение этого вопроса также решило бы этот другой вопрос , как отметил @Gilles. Однако я пришел к выводу, что невозможно делать то, что просили здесь и там. При использовании 2>&1
оба потока корректно объединяются на уровне pty / pipe, но чтобы использовать потоки отдельно и в правильном порядке, действительно следует использовать подход stderred, который включает перехват системных вызовов и может рассматриваться как грязный во многих отношениях.
Я буду стремиться обновить этот вопрос, если кто-то может опровергнуть вышеизложенное.
источник
Ответы:
Вы можете использовать сопроцессы. Простая оболочка, которая передает оба вывода данной команды в два
sed
экземпляра (один дляstderr
другого дляstdout
), которые выполняют тегирование.Обратите внимание на несколько вещей:
Это волшебное заклинание для многих людей (включая меня) - по причине (см. Связанный ответ ниже).
Нет никакой гарантии, что он не поменяет местами пару строк - все зависит от планирования сопроцессов. На самом деле, почти гарантировано, что в какой-то момент времени это произойдет. Тем не менее, если придерживаться строго одинакового порядка, вы должны обрабатывать данные из обоих
stderr
иstdin
в одном и том же процессе, иначе планировщик ядра может (и будет) испортить его.Если я правильно понимаю проблему, это означает, что вам нужно будет указать оболочке перенаправить оба потока на один процесс (что может быть сделано AFAIK). Проблема начинается, когда этот процесс начинает решать, что делать дальше: ему нужно будет опросить оба источника данных и в какой-то момент перейти в состояние, при котором он будет обрабатывать один поток, и данные поступят в оба потока до его завершения. И это именно то, где оно ломается. Это также означает, что упаковка выходных системных вызовов, как
stderred
, возможно, является единственным способом достижения желаемого результата (и даже в этом случае у вас могут возникнуть проблемы, когда что-то станет многопоточным в многопроцессорной системе).Что касается сопроцессов, обязательно прочитайте превосходный ответ Стефана в разделе Как вы используете команду coproc в Bash? для глубокого понимания.
источник
./test1.sh: 3: ./test1.sh: Syntax error: "(" unexpected
путем копирования / вставки вашего скрипта)bash
с этим/bin/sh
(не уверен, почему он у меня там был).eval $@
довольно глючит Используйте,"$@"
если вы хотите, чтобы ваши аргументы были точной командной строкой - добавление слояeval
интерпретации добавляет кучу трудно предсказуемых (и потенциально вредоносных, если вы передаете имена файлов или другой контент, который вы не контролируете как аргументы) поведения и неспособность кавычки еще больше (разбивает имена с пробелами на несколько слов, расширяет глобусы, даже если они ранее цитировались как буквальные и т. д.).eval
закрывать файловые дескрипторы, названные в переменной.exec {SEDo[1]}>&-
будет работать как есть (да, отсутствие$
до того{
было преднамеренным).Способ № 1. Использование файловых дескрипторов и awk
Как насчет этого, используя решения из этой SO Q & A под названием: Существует ли утилита Unix для добавления меток времени к строкам текста? и это SO Q & A под названием: pipe STDOUT и STDERR для двух разных процессов в сценарии оболочки? ,
Подход
Шаг 1, мы создаем 2 функции в Bash, которые при вызове будут выполнять отметку времени:
Шаг 2: вы можете использовать вышеуказанные функции, например, чтобы получить желаемое сообщение:
пример
Здесь я придумал пример, который будет писать
a
в STDOUT, спит в течение 10 секунд, а затем записывает вывод в STDERR. Когда мы помещаем эту последовательность команд в нашу конструкцию выше, мы получаем сообщения, как вы указали.Способ № 2. Использование аннотированного вывода
Существует инструмент под названием,
annotate-output
который является частьюdevscripts
пакета, который будет делать то, что вы хотите. Единственное ограничение заключается в том, что он должен запускать сценарии для вас.пример
Если мы поместим наш пример последовательности команд в скрипт, который называется
mycmds.bash
так:Затем мы можем запустить его так:
Формат вывода можно контролировать для части временной отметки, но не более того. Но это похоже на то, что вы ищете, так что это может соответствовать всем требованиям.
источник
stderred
вами не может легко определить границы линий (попытка так была бы хакерской). Я хотел посмотреть, сможет ли кто-нибудь помочь мне с этой проблемой, но, очевидно, все хотят отказаться от единственного ограничения ( порядка ), которое является основой для вопроса