[bash] Раздельныое перенаправление stdout и stderr в другие программы

SCIF32

Такое можно сделать?
типа пайпа "|" только два одновременно :)
как слить в один поток я знаю, но это не интересно

procenkotanya

Да, такое можно сделать.
И да, вы только что получили абсолютно правильный и (почти) бесполезный ответ :-P

SCIF32

:grin:
уточняю вопрос: и как же это сделать?

Serab

Самому написать. Программа - 30 строк (от души).

SCIF32

как следить за отвалившимся основным процессом, сколько дополнительных процессов оно породит в течении работы основной программы?
30 строк - много для такой задачи, поэтому хочется в одну )

procenkotanya

Вот примерчег:
/usr/bin/time ls > >(cat > stdout) 2> >(cat > stderr)

SCIF32

о, спасибо, не знал про подобный синтаксис, теперь буду :)

rosali

пользуясь этими чудовозможностями bash ты пройдешь по стольким граблям сколько тебе и не снилось ;)
задумался ли ты например о том, что привычный пайп (x | y) завершается тогда, когда оба процесса x и y завершились
 
[xoft ~]$ time (sleep 3) | (sleep 5)

real 0m5.002s
user 0m0.004s
sys 0m0.008s

[xoft ~]$ time (sleep 5) | (sleep 3)

real 0m5.002s
user 0m0.000s
sys 0m0.004s

а вот конструкция (x > >(y) 2> >(z заканчивается вместе с x и процессы y и z продолжают висеть в воздухе.

[xoft ~]$ time (sleep 3 > >(sleep 5) 2> >(sleep 4

real 0m3.003s
user 0m0.000s
sys 0m0.004s

и поверь мне это лишь десятая часть предстоящих тебе открытий :)

Ivan8209

> а вот конструкция (x > >(y) 2> >(z заканчивается вместе с x
> и процессы y и z продолжают висеть в воздухе.
Конец файла не спасает?
---
Q8: А где это есть и круче?
A8: В VMS!

rosali

ну кагбе зависит. если например "y" это sort, то он сперва вычитает весь stdin, а потом еще будет домерживать в фоне. если я правильно понял о чем ты :)

Ivan8209

Ну так, он ведь так всегда делает, точно то же будет и для
простого перенаправления.
Я не понял, где ты видишь грабли, если таковыми не считать
гениальное решение в виде комбинации асинхронного сигнала и
признака конца файла.
---
Q7: А что за suxx?
A7: unix.

rosali

ну хотя бы то, что ты не можешь контролировать коды возврата. в обычном то пайпе код левой части теряется, ну это еще как-то можно стерпеть, типа часто если левая часть сломалась то и правая сломается и это можно заметить. а тут так вообще тебе возвращается код "x"-а, а "y" и "z" еще вообще не доработали. напихаешь чего-нибудь в sort, а ему потом места не хватит в /tmp/ чтобы досортироваться, ты об этом даже не узнаешь.

Dasar

Ну так, он ведь так всегда делает, точно то же будет и для
простого перенаправления.
вот такое отработает

type A | sort > q
type q | more

а вот такое, насколько я понял, уже может и не отработать (т.к. управление первая строка уже вернет, но запись q до конца еще не выполнится)

type A > (sort > q)
type q | more

SCIF32

а вот конструкция (x > >(y) 2> >(z заканчивается вместе с x и процессы y и z продолжают висеть в воздухе.
задумался, конечно, собственно поэтому вопрос про пайп и был.
только вот я ее позапускал, с первого раза такое не заметил,
с концами всё в порядке у меня, еще посмотрю.

Ivan8209

"produce | sort | uniq"
Расскажи, чем твоё "напихаешь чего-нибудь в sort" отличается от этого?
Ты уверен, что ты не переоцениваешь возможностей обычного sh?
Надеюсь, ты понимаешь, как эти конструкции сделаны на (v)fork+pipe?
Расскажешь, как такое сделать в sh и получить все коды завершения?
---
A44: Ламеры в гамаке пусть в тапках трахаются --- это их проблемы.
Я в своём гамаке хочу полноценно трахаться на лыжах.

Ivan8209

> а вот такое, насколько я понял
Если ты не понимаешь вопроса, может, не стоит лезть со своими гаданиями?
Расскажи, что происходит на конвейере "P | C" в случаях:
а) когда P завершается раньше C;
б) когда C завершается раньше P.
---
"Vyroba umelych lidi, slecno, je tovarni tajemstvi."

vall

очевидно завернуть каждую стадию в сабшелл в комплекте с велосипедом
именно из-за этого в баше добавили pipefail и PIPESTATUS чтоб не городить велосипеды ради простой конструкции.

rosali

> produce | sort | uniq
> чем твоё "напихаешь чего-нибудь в sort" отличается от этого
да нет я ничего не говорю :) ничем особенно не отличается. отличается если просто
produce | sort > x
ну хоть на том спасибо :)
я всю жизнь буду помнить как была программа
set -e
generate
process
distribute
(если кто не знает, set -e означает прерывать работу скрипта по первому плохому коду возврата. то есть не смогли успешно сgenerate-ить - не process-им, не смогли успешно с-process-ить - не distribute-им). а потом мне не понравилось что process слишком много мусора в stout сыплет, и я дописал ему
process | grep -v bullshit
ну и дальше неделю разбирались почему периодически всем рассылаются недостроенные данные :-\ grep то полюбому успешно завершается ;)
> как такое сделать в sh и получить все коды завершения
ну как как

[xoft ~]$ cat ./xxx.sh
#!/bin/bash
rm /tmp/xxx && mkfifo /tmp/xxx
rm /tmp/yyy && mkfifo /tmp/yyy
bash -c "$1" >/tmp/xxx &
PID1=$!
bash -c "$2" </tmp/xxx >/tmp/yyy &
PID2=$!
bash -c "$3" </tmp/yyy &
PID3=$!
wait $PID1
CODE1=$?
wait $PID2
CODE2=$?
wait $PID3
CODE3=$?
echo $CODE1 $CODE2 $CODE3

[xoft ~]$ ./xxx.sh "true" "false" "true"
0 1 0

[xoft ~]$ ./xxx.sh "false" "true" "true"
1 0 0

[xoft ~]$ ./xxx.sh "echo a" "tee /dev/stdout" "tee /dev/stdout"
a
a
a
a
0 0 0

=) нет я правда не знаю другого способа. хотя это конешно уныло :) я лично когда нужны навороты такого уровня сложности пользуюсь перловыми open2/open3. но тоже своих граблей хватает, вечные дедлоки и прочие радости. эх нет в мире счастья =)

Dasar

Расскажи, что происходит на конвейере "P | C" в случаях
если верит druxa, то конвеер ждет, когда завершится и P, и C.

[xoft ~]$ time (sleep 3) | (sleep 5)

real 0m5.002s

[xoft ~]$ time (sleep 5) | (sleep 3)

real 0m5.002s


или приведенные druxa факты не верны?

rosali

ну КОНТРА видимо намекает, что если процесс C завершится, а P попытается что-то написать в stdout то он словит SIGPIPE

[xoft ~]$ (perl -e '$SIG{PIPE} = sub {warn "sigpipe!\n"}; sleep 2; $|=1; print "a\n";') | (sleep 1)
sigpipe!

Ivan8209

> нет я правда не знаю другого способа. хотя это конешно уныло
Ну да. Теперь, когда мы выяснили, что в простом конвейере для
получения кода завершения любого процесса, кроме последнего,
надо прилагать заметные усилия, объясни, где ты увидел какие-то
_особенные_ грабли?
D> привычный пайп (x | y) завершается тогда, когда оба процесса
D> x и y завершились
Задумывался ли ты над тем, что вот это твоё утверждение может
быть неверным?
---
Q6: Я слышал есть такой мужик, вроде Бармин зовут,
и он придумал что-то такое после чего XXX не сосет.

Ivan8209

>> Расскажи, что происходит на конвейере "P | C" в случаях
> если верит druxa, то конвеер ждет, когда завершится и P, и C.
А если не верить? Что происходит на самом деле?
---
"Vyroba umelych lidi, slecno, je tovarni tajemstvi."

hwh2010

Задумывался ли ты над тем, что вот это твоё утверждение может быть неверным?
а может быть и верным:
(trap "" PIPE; sleep 5; ps a|grep echo|grep -v grep|wc -l >/dev/stderr; sleep 5; trap - PIPE;)|echo kuku

Ivan8209

> а может быть и верным
Твой пример ничего не доказывает.
Что происходит, когда в случае "A & B | C" процесс "A"
завершается раньше "B", игнорирующего SIGPIPE?
---
"Narrowness of experience leads to narrowness of imagination."
P.S. Я не просто так спросил, мне лень копаться в исходниках bash,
тем более, что меня это не волнует, а в исходники sh я заглянул.
Там, как и ожидалось, "waitpid(-1, status, flags)".

hwh2010

"A & B | C"
ёма-народ! это отличается как от (A & B) | C, так и от A & (B | C)
:confused:
низнаю, ушёл ботать синтаксис

SCIF32

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

rosali

сохрани во временные файлы и потом обработай. и не парься :)

SCIF32

да это для логов надо было. уже на питоне написал :)

pitrik2

да это для логов надо было. уже на питоне написал :)
?
ты точно уверен что тебе команды tee не хватает?

SCIF32

ну дела обстоят так:
есть программа, которая валит логи в stdout и stderr
по некоторым причинам, задача переписывать ее так, чтобы она нормально писала логи в файл, представляется нерешаемой.
более того, хочется писать логи не в файл, а в syslog, при этом дописывая к stderr-у, что это ошибки.
tee тут вроде не спасает никак.

procenkotanya

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

Ivan8209

Имеется в виду, что в их хвалёном bash сделана ошибка, из-за
которой основной процесс не ждёт завершения _команды_.
Там тоже "тот же запах затхловатый," то есть используется,
наверное, тот же waitpid(-1, ... но сделано оно даже не
как конвейер, а как простое перенаправление. Соответственно,
команда считается завершённой при выходе основного процесса,
а "перенаправления" остаются работать.
---
"This user is BSD-compliant."

Serab

Всегда казалось, что ждет он последнего процесса в конвейере.

procenkotanya

Имеется в виду, что в их хвалёном bash сделана ошибка, из-за
которой основной процесс не ждёт завершения _команды_.
Хм, ты уверен, что именно это имел в виду? Почему для него это важно? Если стоит задача как-то обработать логи с stdout и stderr, не всё ли равно, что будет наблюдаться в окружающем шелле, если после смерти основного процесса обработчики логов словят EOF и сдохнут?

procenkotanya

Всегда казалось, что ждет он последнего процесса в конвейере.
*facepalm*
весь тред о том, что A > >(B) не ждёт завершения В

SCIF32

весь тред о том, что A > >(B) не ждёт завершения В
в общем да, я об этом и написал.
у меня процесс может завершиться, не послав конец файла.
судя по всему, при SIGTERM-е такое имеет место быть.
при других сигналах видимо тоже

procenkotanya

у меня процесс может завершиться, не послав конец файла.
судя по всему, при SIGTERM-е такое имеет место быть.
Чего-чего? А нормальные процессы при завершении во все открытые файловые дескрипторы пишут магическую величину "конец файла"? Я всегда думал, что после смерти процесса операционка за ним всё подчищает, так что читающий из пайпа процесс естественным образом получает EOF после смерти писателя и опустошения пайпа.
Можно какой-нибудь пример, демонстрирующий обратное поведение?

Serab

EOF придет, если закрыты все дескрипторы пайпа.

Ivan8209

> Всегда казалось, что ждет он последнего процесса в конвейере.
Ты вот это прочитал?
K> P.S. Я не просто так спросил, мне лень копаться в исходниках bash,
K> тем более, что меня это не волнует, а в исходники sh я заглянул.
K> Там, как и ожидалось, "waitpid(-1, status, flags)".
---
"Vyroba umelych lidi, slecno, je tovarni tajemstvi."

Ivan8209

> EOF придет, если закрыты все дескрипторы пайпа.
Если дескриптор, из которого ты читаешь, закрыт, приходит EBADF,
а не просто EOF.
---
"Vyroba umelych lidi, slecno, je tovarni tajemstvi."

procenkotanya

EOF придет, если закрыты все дескрипторы пайпа.
И что? Пусть каждый из дескрипторов пайпа открыт в одном процессе. Пишущий процесс умирает, операционка закрывает соответствующий fd.

Ivan8209

>> Имеется в виду, что в их хвалёном bash сделана ошибка, из-за
>> которой основной процесс не ждёт завершения _команды_.
> Хм, ты уверен, что именно это имел в виду?
Он мог иметь в виду что-то другое, но мы-то знаем?
> Почему для него это важно? Если стоит задача как-то обработать
> логи с stdout и stderr, не всё ли равно, что будет наблюдаться
> в окружающем шелле, если после смерти основного процесса
> обработчики логов словят EOF и сдохнут?
Я не знаю, в чём проблема. Если от результата этой странной
команды ничего не зависит, не вижу особых причин не использовать
эту погнутую фичу. Мало ли сколько гнусных хаков приходится делать?
Проблемы будут, если у тебя есть какие-то блокировки, барьеры и т.п.
---
"Утверждаю, что с научной точки зрения, главное в профессии вора,
как и в профессии святого, конечно, это вовремя скрыться."

SCIF32

два скрипта
logger.sh
 
 
#!/bin/bash
./test.sh 1> >(logger -it out ) 2> >(logger -it err)

test.sh
 
 
#!/bin/bash
seq 1 10
sleep 3
seq 10 20 > /dev/stderr
sleep 3
seq 20 30
 

для проверки выполняем:
./logger.sh & sleep 1; killall test.sh; sleep 1 ; ps -A|grep logger

procenkotanya

для проверки выполняем:
./logger.sh & sleep 1; killall logger.sh; sleep 1 ; ps -A|grep logger
И что? test.sh живёт после этого ещё 5 секунд, как и оба logger'a. Не понимаю, что должен был продемонстрировать этот пример, ведь пишущий процесс не умирал.

SCIF32

да, да, это опечатка, надо test.sh убивать

procenkotanya

да, да, это опечатка, надо test.sh убивать
ну тогда и logger'ы с какой-то небольшой задержкой дохнут

SCIF32

с какой и от чего она зависит?
ну то есть, ты действительно прав, они со временем отваливается и в первый раз когда я тестил,
от того и не заметил, что все-таки какое-то время они висят
в общем уже то, что больше 5 секунд висит - меня не устраивает )

procenkotanya

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

SCIF32

слабо верится,
поскольку за 10 секунд он у меня не успевал отвалиться,
проц не был загружен

procenkotanya

поскольку за 10 секунд он у меня не успевал отвалиться,
Если логгер специально спит по 10 секунд между обработками порций данных, противоречия нет
или если спит по секунде, но в буфере остаётся 10 порций данных

SCIF32

запусти, чтоли,
не спит логгер ни разу )

procenkotanya

На моей машине за секунду логгеры успевают подохнуть:
$ ./logger.sh & sleep 1; killall test.sh; sleep 1 ; sleep 1; ps -A|grep logger
[1] 8655
./logger.sh: line 2: 8657 Terminated ./test.sh > >(logger -it out ) 2> >(logger -it err)
[1]+ Exit 143 ./logger.sh
$

SCIF32

 :crazy:
тогда хз, буду думать, где косячу

procenkotanya

Хм, заметил, что у меня в командной строке два sleep'а затесалось, так что я ранее немного слукавил ненарочно, сорри )
OH SHI~
$ ./logger.sh & sleep 1; killall test.sh; sleep 1; sleep 1; ps -A|grep logger
[1] 8736
./logger.sh: line 2: 8738 Terminated ./test.sh > >(logger -it out ) 2> >(logger -it err)
[1]+ Exit 143 ./logger.sh
$ ./logger.sh & sleep 1; killall test.sh; sleep 1; ps -A|grep logger
[1] 8750
./logger.sh: line 2: 8751 Terminated ./test.sh > >(logger -it out ) 2> >(logger -it err)
[1]+ Exit 143 ./logger.sh
 8752 pts/11 00:00:00 logger.sh
 8753 pts/11 00:00:00 logger
 8755 pts/11 00:00:00 logger.sh
 8756 pts/11 00:00:00 logger
$
Уму нерастяжимо.

Ivan8209

> Уму нерастяжимо.
А почему у вас "ps A" состояние процесса не выводит?
Так же вообще ничего не видно!
---
"This user is BSD-compliant."

procenkotanya


$ ./logger.sh & sleep 1; killall test.sh; sleep 1; ps auwx|grep logger
[1] 9088
./logger.sh: line 2: 9090 Terminated ./test.sh > >(logger -it out ) 2> >(logger -it err)
[1]+ Exit 143 ./logger.sh
9091 0.0 0.0 10592 560 pts/11 S 16:56 0:00 /bin/bash ./logger.sh
9092 0.0 0.0 5096 612 pts/11 S 16:56 0:00 logger -it out
9093 0.0 0.0 10592 560 pts/11 S 16:56 0:00 /bin/bash ./logger.sh
9094 0.0 0.0 5096 516 pts/11 S 16:56 0:00 logger -it err
$ ./logger.sh & sleep 1; killall test.sh; sleep 2; ps auwx|grep logger
[1] 9101
./logger.sh: line 2: 9103 Terminated ./test.sh > >(logger -it out ) 2> >(logger -it err)
[1]+ Exit 143 ./logger.sh
9113 0.0 0.0 5348 692 pts/11 R+ 16:56 0:00 grep --colour=auto logger
$

Хм, двух секунд стабильно хватает, чтобы логгеры подохли.

rosali

ладно вот вам другая загадка ;)

[xoft ~]$ (sleep 1 1> >(echo a) 2> >(echo b
a
[xoft ~]$

а "b" куда подевалось? O_o
ага, никаких "особенных" граблей :) ппц короче. я ответ знаю, но допирал до него несколько минут.

procenkotanya

Wow. Крутой спецэффект и на самом деле хорошая задачка.
Потенциальный хинт:

vall

другой хинт:

vall

другой вариант использования =)
$ exec > >(grep foo)
$ echo foo
foo
$ echo bar
$ echo foo bar
foo bar
Оставить комментарий
Имя или ник:
Комментарий: