Как перенаправить stdout и stderr в файл и вывести stderr на консоль?

18

Я знаю, как перенаправить в файл и использовать tee; на базовом уровне. Так

$ alias outanderr='bash -c "echo stdout >&1; echo stderr >&2"'
# A fake "application" displaying both output and error messages.

$ outanderr 1>file      # redirect stdout to a file, display stderr
stderr

$ outanderr 2>file      # redirect stderr to a file, display stdout
stdout

$ outanderr 1>file 2>&1 # redirect both to a file, display nothing

$ outanderr | tee file; echo "-- file contents --" && cat file
# redirect stdout to a file, display both (note: order is messed up)
stderr
stdout
-- file contents --
stdout

$ outanderr 2>&1 | tee file; echo "-- file contents --" && cat file
# redirect both to a file, display both
stdout
stderr
-- file contents --
stdout
stderr

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

$ outanderr ???; echo "-- file contents --" && cat file
# redirect both to a file, display stderr
stderr
-- file contents --
stdout
stderr

Constaints:

  • Предполагая Баш.
  • Заказ должен быть сохранен в файле.
  • Содержимое stderr отображается в режиме реального времени построчно, т.е. без буферизации.
  • Можно использовать отдельные файлы сценариев.
  • Магия может быть необходима.
TWiStErRob
источник
Какой контроль над outanderrпрограммой у вас есть?
Кевин
1
@Kevin Я думаю, что вопрос более общий, чем этот. Вот outanderrтолько псевдоним, который печатает строку для stdout, а другую - для stderr. Идея (если это возможно) состоит в том, чтобы создать общее решение, которое могло бы работать с любой программой, не изменяя их.
lgeorget
@ lgeorget Я понимаю это, но я не верю, что в общем решении возможно строго соблюдать все ограничения, поэтому я проверял, можем ли мы получить конкретное решение.
Кевин
@ Igeorget прав.
TWiStErRob

Ответы:

12
2>&1 >>outputfile | tee --append outputfile

Для простого тестирования:

echo -n >outputfile; bash -c "echo stdout >&1; echo stderr >&2" 2>&1 >>outputfile |
  tee --append outputfile; echo "outputfile:"; cat outputfile

Изменить 1:

Это работает путем записи stdout (только) в файл, создания стандартного strout так, чтобы он проходил по каналу, и, когда tee записывает свой вывод в тот же файл.

Обе записи должны выполняться в режиме добавления ( >>а не в >противном случае), в противном случае обе записи будут перезаписывать выходные данные друг друга.

Поскольку канал является буфером, нет никакой гарантии, что выходные данные появятся в файле в правильном порядке. Это даже не изменится, если приложение будет подключено к обоим файловым дескрипторам (два канала). Для гарантированного порядка оба выхода должны проходить через один и тот же канал и иметь соответствующую маркировку. Или вам понадобятся действительно модные вещи:

  1. Если и stdout, и stderr были перенаправлены в файл (а не в один и тот же файл!) И оба файла находились на томе FUSE, тогда модуль FUSE мог бы пометить каждую отдельную запись временной меткой, чтобы второе приложение могло правильно отсортировать данные и объединить их. для реального выходного файла. Или вы не помечаете данные, но модуль создает комбинированный выходной файл. Скорее всего, еще нет модуля FUSE, который делает это ...
  2. И stdout, и stderr могут быть направлены на /dev/null. Выходные данные приложения будут разделены путем его запуска strace -f -s 32000 -e trace=write. В этом случае вам придется поменять побег. Само собой разумеется, что приложение не работает быстрее, будучи отслеженным.
  3. Возможно, того же можно достичь, используя существующий простой модуль FUSE и отслеживая модуль вместо приложения. Это может быть быстрее, чем отслеживание приложения, потому что (или, скорее: если) модуль, вероятно, имеет намного меньше системных вызовов, чем приложение.
  4. Если само приложение можно изменить: приложение может быть остановлено после каждого выхода (но я думаю, что это возможно только изнутри) и продолжаться только после получения сигнала s (SIGUSR1 или SIGCONT). Приложение, считывающее данные из канала, должно будет проверять наличие новых данных и в канале, и в файле и отправлять сигнал после каждых новых данных. В зависимости от типа приложения это может быть быстрее или даже медленнее, чем метод strace. FUSE будет решением для максимальной скорости.
Хауке Лагинг
источник
1
Ба. Поймай меня в середине написания точно того же ответа, почему не так.
Кевин
2
Обратите внимание, что это условие гонки, представляющее возможность замены линий / ошибок, но я не думаю, что этого можно избежать.
Кевин
1
@Kevin Это случается с лучшими из нас, я страдал от этого раньше и чуть не попросил функцию «покажи мне, что кто-то пишет» (хотя это будет сложно). Мне кажется, что состояние гонки возникает, только если запись в файл (stdout) происходит после записи в конвейер.
Хауке Лагинг
Будет не то, что отправить как stdoutи stderrк tee, или я что - то отсутствует? Я думаю, что требование ОП tee stderrтолько.
Джозеф Р.
@JosephR. Вы случайно не пробовали?
Хауке Лагинг