Модераторы: korob2001, ginnie

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Перенаправление stdout+stderr, с предварительной обработкой 
:(
    Опции темы
CaptainFlint
Дата 7.12.2006, 12:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Искатель сокровищ
**


Профиль
Группа: Участник
Сообщений: 393
Регистрация: 26.2.2003
Где: Москва

Репутация: нет
Всего: 23



Система - Linux.
Перл-скрипт изнутри себя запускает приложение, исходники которого мне недоступны. Это приложение пишет и в stdout, и в stderr. Задача такова: перехватить эти два потока и выводить их в файл построчно, отмечая в начале каждой строки, из которого потока она пришла. Например:
Код
o: Это строчка была выведена в stdout
o: Эта туда же
e: А эта - в stderr
o: Снова stdout

При этом главное - сохранить порядок строк таким же, каким он был бы при стандартном выводе обоих потоков в терминал.

Первую-то проблему я решил (через два именованных пайпа), но, к сожалению, если строки выводятся в stdout и stderr слишком часто, их порядок в выходном файле может нарушиться (из двух процессов, висящих на чтении пайпов, первым получает управление случайно выбранный, а не тот, на чей пайп раньше пришли данные).

Вопрос: можно ли как-то решить эту задачу? Я понимаю, что в общем случае, для сложных систем полная синхронизация невозможна, но если программа пишет:
Код
print STDOUT "Пытаемся открыть файл...\n";
print STDERR "Ошибка открытия файла!\n" if (!open(FILE, "/tmp/asdf"));

то в выходном файле они ни в коем случае не должны оказываться в обратном порядке! В терминал-то выводится всё правильно...


--------------------
Почему же, ё-моё, ты нигде не пишешь "ё"?
--------------------
Для тех, кто не знает: CaptainFlint - это бывший Константин. ;) 
PM MAIL WWW ICQ   Вверх
Nab
Дата 7.12.2006, 12:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 582
Регистрация: 25.3.2006
Где: Kiev

Репутация: 26
Всего: 37



Я тут вижу два варианта.

Первый, это сразу сделать 2>&1 , но тут трабла, что выходной поток вам самим прийдется анализировать, и определять какое сообщение какому потоку соответствует, потому как вам они будут валиться в один поток.

Второй, это временные метки, вам самому возможно прийдется буферизировать и сортировать на выходе.
Задача не очень простая smile

PS: Есть еще один вариант smile отключте буферизацию всех потоков, как входных так и выходных...
Только обсуждали в соседнем топике
Как гарантировано закрыть файл
Возможно поможет...



--------------------
 Чтобы правильно задать вопрос нужно знать больше половины ответа...
Perl Community 
FREESCO in Ukraine 
PM MAIL   Вверх
DiverD
Дата 7.12.2006, 13:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 225
Регистрация: 14.9.2005
Где: Белорусь

Репутация: 1
Всего: 2



если я тебя правильно понял , томожет тебе нужна воспользоваться 
Код

flock
 ?
--------------------
[ FreeBSD & pERL p0WER eVERY dAY ]
PM MAIL   Вверх
CaptainFlint
Дата 7.12.2006, 13:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Искатель сокровищ
**


Профиль
Группа: Участник
Сообщений: 393
Регистрация: 26.2.2003
Где: Москва

Репутация: нет
Всего: 23



Цитата(Nab @  7.12.2006,  13:58 Найти цитируемый пост)
Первый, это сразу сделать 2>&1 , но тут трабла, что выходной поток вам самим прийдется анализировать, и определять какое сообщение какому потоку соответствует, потому как вам они будут валиться в один поток.

Вот-вот, это никак не подходит, потому как никаких сигнальных строк там не содержится. Т.е. выцепить из объединённого потока нужные строки невозможно.

Цитата(Nab @  7.12.2006,  13:58 Найти цитируемый пост)
Второй, это временные метки, вам самому возможно прийдется буферизировать и сортировать на выходе.

Я бы рад, но именно это и не получается сделать. Какой из процессов-слушателей раньше получит управление, тот и впихнёт более раннюю временную метку, так что толку от этого... smile

Цитата(Nab @  7.12.2006,  13:58 Найти цитируемый пост)
PS: Есть еще один вариант  отключте буферизацию всех потоков, как входных так и выходных...

Уже давно отключил (через $| ), всё равно смешиваются.


Цитата(DiverD @  7.12.2006,  14:00 Найти цитируемый пост)
если я тебя правильно понял , томожет тебе нужна воспользоваться flock?

Пользуюсь. От более раннего вызова это не спасает.

Возможно, конечно, я ошибаюсь, т.к. никогда глубоко в этом не копался, но, как мне кажется, проблема в следующем:
Есть два процесса, один слушает пайп, прицепленный к stdout'у пишущего приложения, второй слушает пайп, прицепленный к stderr'у. Большую часть времени оба они висят, заблокированные в функции read, пока приложение чего-нибудь не запишет. И вот, приложение в течение одного кванта времени успевает записать и в OUT, и в ERR. Наступает следующий квант (или просто по сигналу срабатывает планировщик), видит, что есть два висящих процесса, оба готовы к разблокировке. И по каким-то своим алгоритмам один из этих процессов разблокирует. И этим процессом вовсе не обязательно оказывается тот, в который данные были послалы раньше. В этом случае хоть flock, хоть что другое, уже не поможет. Надо каким-то образом умудриться заставить сработать нужный процесс сразу же, как только данные появились в пайпе. Но вот как это сделать...


--------------------
Почему же, ё-моё, ты нигде не пишешь "ё"?
--------------------
Для тех, кто не знает: CaptainFlint - это бывший Константин. ;) 
PM MAIL WWW ICQ   Вверх
CaptainFlint
Дата 7.12.2006, 13:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Искатель сокровищ
**


Профиль
Группа: Участник
Сообщений: 393
Регистрация: 26.2.2003
Где: Москва

Репутация: нет
Всего: 23



Нарыл сейчас функцию open3 (модуль IPC::Open3), но с ней в точности та же проблема. Просто оказываются готовыми к чтению сразу два хэндла, и который из них пришёл раньше - фиг определишь. smile

PS: Упс... А почему сообщения не склеились?..

Это сообщение отредактировал(а) CaptainFlint - 7.12.2006, 13:53


--------------------
Почему же, ё-моё, ты нигде не пишешь "ё"?
--------------------
Для тех, кто не знает: CaptainFlint - это бывший Константин. ;) 
PM MAIL WWW ICQ   Вверх
Nab
Дата 7.12.2006, 14:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 582
Регистрация: 25.3.2006
Где: Kiev

Репутация: 26
Всего: 37



ну тады пробуй так:
сделай ему tee в третий поток, и слушай три сразу, стандартный, ошибок, и общий, по которому и будешь определять порядок smile



--------------------
 Чтобы правильно задать вопрос нужно знать больше половины ответа...
Perl Community 
FREESCO in Ukraine 
PM MAIL   Вверх
CaptainFlint
Дата 7.12.2006, 15:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Искатель сокровищ
**


Профиль
Группа: Участник
Сообщений: 393
Регистрация: 26.2.2003
Где: Москва

Репутация: нет
Всего: 23



Цитата(Nab @  7.12.2006,  15:54 Найти цитируемый пост)
сделай ему tee в третий поток, и слушай три сразу, стандартный, ошибок, и общий, по которому и будешь определять порядок

Не подскажешь командную строку, как такое сотворить? Я уже думал над таким вариантом, но что-то не получается...


--------------------
Почему же, ё-моё, ты нигде не пишешь "ё"?
--------------------
Для тех, кто не знает: CaptainFlint - это бывший Константин. ;) 
PM MAIL WWW ICQ   Вверх
Nab
Дата 7.12.2006, 16:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 582
Регистрация: 25.3.2006
Где: Kiev

Репутация: 26
Всего: 37



Ну вот есть команда tee, но как правильно ее записать фиг ее знает smile
http://www.opennet.ru/docs/RUS/shutil/shellutils_6.html
http://www.opennet.ru/man.shtml?topic=tee&...1&russian=0

по идее вместо файла можно указать пайп спецом перед эти созданный, и читать с него....




--------------------
 Чтобы правильно задать вопрос нужно знать больше половины ответа...
Perl Community 
FREESCO in Ukraine 
PM MAIL   Вверх
CaptainFlint
Дата 7.12.2006, 20:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Искатель сокровищ
**


Профиль
Группа: Участник
Сообщений: 393
Регистрация: 26.2.2003
Где: Москва

Репутация: нет
Всего: 23



Nab
М-да... Маны-то я и до этого читал... Похоже, толку здесь от tee не будет. smile
Если я stdout и stderr с его помощью куда-то перенаправлю (хоть в один файл, хоть в два, хоть в три), они всё равно будут обрабатываться двумя разными процессами (как минимум), и эти два процесса уже будут получать данные вразнобой.

С ума сойти, неужели такая элементарная задача не имеет решения? smile 


--------------------
Почему же, ё-моё, ты нигде не пишешь "ё"?
--------------------
Для тех, кто не знает: CaptainFlint - это бывший Константин. ;) 
PM MAIL WWW ICQ   Вверх
Nab
Дата 7.12.2006, 20:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 582
Регистрация: 25.3.2006
Где: Kiev

Репутация: 26
Всего: 37



Покаж код плиз, которым читаешь из потоков, здается мне, мы не там ищем...


--------------------
 Чтобы правильно задать вопрос нужно знать больше половины ответа...
Perl Community 
FREESCO in Ukraine 
PM MAIL   Вверх
CaptainFlint
Дата 7.12.2006, 21:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Искатель сокровищ
**


Профиль
Группа: Участник
Сообщений: 393
Регистрация: 26.2.2003
Где: Москва

Репутация: нет
Всего: 23



writer.pl:
Код

#!/usr/bin/perl -w

system("mknod", "/tmp/test_pipe_o", "p");
system("mknod", "/tmp/test_pipe_e", "p");

my $oldh = select(STDOUT);
$| = 1;
select(STDERR);
$| = 1;
select($oldh);

print "Begin to write\n";
print STDERR "Begin to stderr\n";
sleep(1);
print "test1\n";
sleep(1);
print "test2\n";
sleep(1);
print STDERR "test stderr\n";
print "test3\n";
sleep(1);
print "test4\n";

print STDERR "Finished stderr\n";
print "Finished.\n";


reader.pl:
Код

#!/usr/bin/perl -w

use Fcntl ":flock";

my $name = shift;
my $prefix = (($name =~ m/test_pipe_e/) ? "e" : "o");
open(FILE, $name) or die "Cannot open pipe $name\n";
open(FILE_OUT, ">>/tmp/result") or die "Cannot open output file\n";

while (<FILE>) {
    flock(FILE_OUT, LOCK_EX);
    print FILE_OUT "$prefix: $_";
    flock(FILE_OUT, LOCK_UN);
}
close(FILE);
close(FILE_OUT);
print "Finished.\n";


Очищаю файл /tmp/result, затем в одном терминале запускаю:
Код

reader.pl /tmp/test_pipe_o


Во втором:
Код

reader.pl /tmp/test_pipe_e


В третьем:
Код

writer.pl 1>/tmp/test_pipe_o 2>/tmp/test_pipe_e


После окончания работы writer'а смотрю результат в /tmp/result. Строки, начинающиеся с "o:" пришли из stdout'а, с "e:" - из stderr'а.


--------------------
Почему же, ё-моё, ты нигде не пишешь "ё"?
--------------------
Для тех, кто не знает: CaptainFlint - это бывший Константин. ;) 
PM MAIL WWW ICQ   Вверх
Nab
Дата 7.12.2006, 23:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 582
Регистрация: 25.3.2006
Где: Kiev

Репутация: 26
Всего: 37



Упс, какой ужас.... smile

Я нашел один рецепт в CookBook, как это сделать...

Код

 (./writer.pl | sed 's/^/o:/' ) 2>&1 | ./reader.pl


То есть мы его помечаем до слияния с выводом ошибок...

во writer.pl я убрал только создание пайпов, а reader.pl таков:
Код

#!/usr/bin/perl -w

use Fcntl ":flock";

my $prefix = shift;

open(FILE_OUT, ">>/tmp/result") or die "Cannot open output file\n";

while (<>) {
    # здесть просто маркируем 'e:' если еще не промаркировано как 'o:'
    s/^/e:/ unless /^o:/;
    print FILE_OUT  $_;
}
close(FILE_OUT);
print "Finished.\n";

1;


чтобы не извращаться с внешней командой, то совет от туда же:
perldoc IPC::Open2 или IPC::Open3.
Туту вообще все внутри одного скрипта делаеться...
и страшные внешние команды писать вообще не надоть... smile

Добавлено @ 23:52 
Нееее, мой вариант, тоже выдает сначала STDERR а потом обычный вывод smile


--------------------
 Чтобы правильно задать вопрос нужно знать больше половины ответа...
Perl Community 
FREESCO in Ukraine 
PM MAIL   Вверх
Nab
Дата 8.12.2006, 00:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 582
Регистрация: 25.3.2006
Где: Kiev

Репутация: 26
Всего: 37



Вот таки передрал рецепт из CookBook:

writer.pl остается таким, а readerюзд таков:
Код

#!/usr/bin/perl -w


use IPC::Open3;
use IO::Select;

open3( *CMD_IN, *CMD_OUT, *CMD_ERR , "./writer.pl") or die "Cannot open writer.pl \n";
close(CMD_IN);

my $selector = IO::Select->new();
$selector->add(*CMD_ERR, *CMD_OUT);



open(FILE_OUT, ">>result") or die "Cannot open output file\n";


while (my @ready = $selector->can_read) {
    foreach my $fn (@ready) {
        if (fileno($fn) == fileno(CMD_ERR)) {
            print FILE_OUT "e: ", scalar <CMD_ERR>
        } else {
            print FILE_OUT "o: ", scalar <CMD_OUT>
        }
        $selector->remove($fn) if eof($fn)
    }
}

close(FILE_OUT);
print "Finished.\n";

1;



Все пашет замечательно, и порядок вполне сохраняется...



--------------------
 Чтобы правильно задать вопрос нужно знать больше половины ответа...
Perl Community 
FREESCO in Ukraine 
PM MAIL   Вверх
Danissimo
Дата 8.12.2006, 11:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 150
Регистрация: 14.8.2006
Где: Санкт-Петербург

Репутация: 1
Всего: 1



Я про фундаментальную невозможность решить эту задачу в том виде, в котором она поставлена в полном объеме. Представьте себе, что у вас 2 независимых процессора. На кождом из них крутиться поток (thread). Пусть первый поток в момент времени t написал в stdout строку. Пусть в то же самое время второй поток написал в stderr. Я утверждаю, что смысл условия "сохранение порядка" не определен, так как события произошли одновременно.

Этим я всего лишь хотел проиллюстрировать фундаментальный принцип многопоточной обработки данных, когда вы не в состоянии сказать, какое событие произошло раньше, если не прибегаете к блокировкам в том или ином виде. Посмотрите формализм Лампорта.

Так как код закрыт, то вы не знаете, сколько потоков выолняют запись в stdout и stderr, следовательно (не) перепутать строки не возможно в принципе. Или знаете, что только один поток пишет в потоки вывода?

Вот такие мысли.

Добавлено @ 11:47 
Ещещ мысль. А что если размеры буферов у потоков stderr и stdout разные? А что если для одного потока автофлаш включен, а длдя другого нет. В этом случае даже для одного потока строки на выходе уже будут перепутанными.
PM MAIL   Вверх
Nab
Дата 8.12.2006, 11:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 582
Регистрация: 25.3.2006
Где: Kiev

Репутация: 26
Всего: 37



Теория эт конечно хорошо, но то что Вы, Danissimo, описываете, как правило относиться к постоянному выводу нескольких равноправных потоков. Вывод в STDERR происходит при ошибке одного из потоков, и соответственно порядок возникновения события ошибки вполне определен. Не думаю что бы кто-то в здравом уме, передавал состояние ошибки в другой поток для вывода, при этом сам продолжая выводить данные...

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

Что в принципе и пытались мы реализовать в этом топике...


--------------------
 Чтобы правильно задать вопрос нужно знать больше половины ответа...
Perl Community 
FREESCO in Ukraine 
PM MAIL   Вверх
Страницы: (3) Все [1] 2 3 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Perl"
korob2001
sharq
  • В этом разделе обсуждаются общие вопросы по языку Perl
  • Если ваш вопрос относится к системному программированию, задавайте его здесь
  • Если ваш вопрос относится к CGI программированию, задавайте его здесь
  • Интерпретатор Perl можно скачать здесь ActiveState, O'REILLY, The source for Perl
  • Справочное руководство "Установка perl-модулей", можно скачать здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, korob2001, sharq.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Perl: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0614 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.