4

Я ищу способ запустить программу Prog и в конечном итоге с 3 файлами:

  1. Стандартный Prog
  2. Стдерр Prog
  3. И stdout, и stderr из Prog объединяются так же, как и на экране, если перенаправление не происходит.

Существует ли комбинация перенаправления, каналов и т.д., Которая могла бы достичь этого?

Примечание: я обычно использую bash.

3 ответа3

4

Насколько я знаю, не существует элегантного способа сделать это (для не элегантного способа, который работает, но не симпатичен, прокрутите вниз до конца моего ответа). Я сомневаюсь, что вы можете сделать лучше, чем:

$ command >stdout.txt 2>stderr.txt && cat stdout.txt stderr.txt > both.txt

Есть несколько интересных трюков, которые вы можете использовать, но ни один из них, похоже, не удастся создать 3 файла лучше, чем указано выше. Основная проблема заключается в том, что файл both.txt не будет отображать сообщения (STDERR и STDOUT) в правильном порядке. Это потому что (как объяснено здесь):

Когда вы перенаправляете стандартный вывод и стандартную ошибку в один и тот же файл, вы можете получить неожиданные результаты. Это связано с тем, что STDOUT является буферизованным потоком, а STDERR всегда небуферизованным. Это означает, что каждый символ STDERR записывается, как только он становится доступным, в то время как STDOUT пишет вещи в пакетном режиме. Когда и STDOUT, и STDERR переходят в один и тот же файл, вы можете увидеть, что сообщения об ошибках появляются раньше, чем вы ожидали, по отношению к фактическому выводу вашей программы или скрипта. Не о чем беспокоиться, а просто побочный эффект буферизованных и небуферизованных потоков, вам просто нужно помнить об этом.

Лучшая альтернатива, которую я мог найти, - это использование bash-подоболочек, довольно сложная и все еще не отображающая вывод в правильном порядке. Я сделал простой Perl-скрипт test.pl который печатает "OUT" в STDOUT и "ERR" в STDERR , повторяя процесс 3 раза:

#/usr/bin/perl 
for($i=0; $i<=2; $i++){
    print STDOUT "OUT\n"; 
    print STDERR "ERR\n"
}

Обычный, не перенаправленный вывод:

$ ./test.pl
OUT
ERR
OUT
ERR
OUT
ERR

Чтобы перенаправить вывод (ы), я запустил:

(./test.pl 2> >(tee error.txt) > >(tee out.txt)) > both.txt 

Это использует tee , программу, которая будет печатать свои данные на экране и в имени файла. Итак, я перенаправляю STDERR и передаю его в качестве входных данных tee , приказывая записать его в файл error.txt . Аналогично с STDOUT и файлом out.txt . Я помещаю все это в подоболочку ((...)), чтобы затем я мог перехватить весь его вывод и перенаправить в both.txt .

Теперь это работает, поскольку создает 3 файла, один с STDERR , один с STDOUT и один с обоими. Однако, как объяснено выше, это приводит к тому, что сообщения отображаются в неправильном порядке в both.txt:

$ cat both.txt 
ERR
ERR
ERR
OUT
OUT
OUT

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

 $(./test.pl \
   2> >(while read n; do echo `date +%N`" $n"; echo "$n" >>error.txt; done) \
    > >(while read n; do echo `date +%N`" $n"; echo "$n" >> out.txt; done )) \
 | gawk '{print $2}'> both.txt 
2

1) создать два скрипта:

getout.sh
geterr.sh

getout.sh получит стандартный вывод.
Скрипт запишет данные в файл f1.txt, а также запишет те же данные в файл f12.txt.

geterr.sh получит Stderr.
Скрипт запишет данные в файл f2.txt, а также запишет те же данные в файл f12.txt.

Я не верю, что можно надежно сохранить точный порядок данных, записанных в файл: f12.txt. Это связано с тем, что записи в Stdout буферизируются операционной системой, а записи в Stderr происходят почти в реальном времени. Это может (будет) задерживать просмотр данных Stdout скриптом. Эффект этого:

a. For some data written to Stdout BEFORE data written to 
   Stderr, the data written to f12.txt from Stderr may 
   precede the data written by Stdout.  
b. Some data written to Stdout will be accumulated with 
   other data written earlier or later. As the script sees it, 
   these data lines may all appear at the same time (together).  
c. Because of (b), for some data written to Stderr in-between 
   multiple data writes to Stdout, the data written to f12.txt 
   from Stderr may precede the whole group of data written to Stdout.  
d. Because of all this, including timestamps with the data 
   written to f12.txt will not preserve the order that events happened.  

2) создать 2 именованных канала:

% mknod pout p
% mknod perr p

3) пусть скрипты "слушают" каналы:

% ./getout.sh < pout &
% ./geterr.sh < perr &

4) Запустите вашу программу (Prog) следующим образом:

% Prog 1> pout 2> perr
0

Чтобы разделить их на разные файлы:

./Prog 1> stdout.log 2> stderr.log

Чтобы они объединялись в один файл:

./Prog 1>> combine.log 2>> combine.log

Существует «сокращенный» способ сделать объединение для bash, но вышеупомянутое безопаснее, если вы используете sh.


Зависит от того, насколько точно ваш комбайн нужен, может работать или не работать следующее

touch stdout.log
touch stderr.log
tail -f stdout.log >> combine.log &
tail -f stderr.log >> combine.log &
./Prog 1>> stdout.log 2>> stderr.log ; pkill tail

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .