여러 프로그램을 병렬로 실행하고 출력을 하나의 파이프에 결합하고 싶다고 가정 해보십시오.
sh -c '
(echo qqq; echo qqq2; echo qqq3)&
(echo www; echo www2; echo www3)&
(echo eee; echo eee2; echo eee3)&
wait; wait; wait'
이 쉘 접근법은이 간단한 경우에는 잘 작동하지만 프로그램이 다음과 같이 버퍼링 된 방식으로 더 많은 라인을 출력하면 실패 할 것으로 예상됩니다 (구성됨).
qqq
qqwww
q2
qqq3www2
wwweee3
eee2
eee3
내가 사용하는 힌트 중 하나는 tail -f
다음과 같습니다.
tail -n +0 -q -f <(echo qqq; echo qqq2; echo qqq3) <(echo www; echo www2; echo www3) <(echo eee; echo eee2; echo eee3)
그러나 이것은 차선책입니다. 데이터가 느리게 출력되고 종료되지 않습니다. 출력은 “슬립”순서가 아니라이 순서에서 인수 순서로 표시됩니다.
tail -n +0 -q -f <(sleep 1; echo qqq; sleep 1; echo qqq2; echo qqq3) <(echo www; echo www2; sleep 10; echo www3) <(echo eee; sleep 4; echo eee2; echo eee3) | cat
나는 이것을 위해 특별한 작은 프로그램을 구현 했지만 그것을하는 표준 좋은 방법이 있어야한다고 생각합니다.
표준 도구를 사용하고 tail -f
단점 없이 어떻게합니까?
답변
에서 릴리스 노트 2013년 8월 일자 :
--line-buffer
출력을 라인 단위로 버퍼링합니다.--group
전체 작업에 대해 출력을 함께 유지합니다.--ungroup
한 작업에서 나오는 반 줄과 다른 작업에서 나오는 반 줄을 출력과 혼합 할 수 있습니다.--line-buffer
이 둘 사이에 맞습니다. 전체 줄을 인쇄하지만 다른 작업의 줄을 혼합 할 수 있습니다.
예를 들면 다음과 같습니다.
parallel --line-buffer <jobs
어디에 jobs
포함 :
./long.sh
./short.sh one
./short.sh two
short.sh
:
#!/bin/bash
while true; do
echo "short line $1"
sleep .1
done
long.sh
:
#!/bin/bash
count=0
while true; do
echo -n "long line with multiple write()s "
sleep .1
count=$((count+1))
if [ $count -gt 30 ]; then
count=0
echo
fi
done
산출:
short line one
short line two
short line one
short line two
short line one
**-snip-**
short line one
short line one
short line two
short line two
short line one
short line one
short line one
long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s
short line two
short line two
short line two
short line one
답변
잠금을 구현하는 솔루션 :
function putlines () {
read line || return $?
while ! ln -s $$ lock >/dev/null 2>&1
do
sleep 0.05
done
echo "$line"
}
function getlines () {
while read lline
do
echo "$lline"
rm lock
done
}
# your paralelized jobs
(
job1 | putlines &
job2 | putlines &
job3 | putlines &
wait
) | getlines| final_processing
파일 시스템을 사용하는 것보다 잠금을 만드는 더 빠른 방법이 있어야합니다.
답변
나는 당신이 당신의 라인이 너무 길면 하나의 프로그램이 가능하기 전에 대기 상태로 보내져 stdout에 라인을 작성하는 것을 도울 수있는 간단한 것을 생각할 수 없습니다.
그러나 프로세스 전환 전에 라인을 완전히 쓸 수있을 정도로 짧고 한 라인을 생성하는 데 시간이 오래 걸리면 read를 사용하여 출력을 버퍼링 할 수 있습니다.
예 :
((./script1 | while read line1; do echo $line1; done) & \
(./script2 | while read line2; do echo $line2; done)) | doSomethingWithOutput
답변
을 사용하여 명명 된 파이프를 만들고 mkfifo
모든 출력을 명명 된 파이프에 덤프하고 수집 된 데이터에 대해 명명 된 파이프에서 별도로 읽을 수 있습니다.
mkfifo /tmp/mypipe
job1 > /tmp/mypipe &
job2 > /tmp/mypipe &
job3 > /tmp/mypipe &
cat /tmp/mypipe > /path/to/final_output &
wait; wait; wait; wait
답변
오래된 질문, 나는 알고 있지만, 나는 이것에 대해 궁금해하고 있으며 이것이 내가 생각해 낸 것입니다.
garbling_job | (
while read LINE
do
echo $LINE
done
) &
나는 잘못된 출력에 대해 걱정할 필요없이 상당히 많은 것을 시작할 수있는 것 같습니다.
여기 내 테스트 프로그램이 있습니다
if [ "$1" = "go" ]
then
for i in 1 2
do
printf 111112222222222223333
sleep .01
printf 3333333444444444444555555555555
sleep .01
printf 6666666666666667777
sleep .01
printf 777777788888888889999999999999999
sleep .01
echo
done
exit
fi
# running them in sequence is all very fine
for i in 1 2 3 4 5 6 7 8
do
echo bash $0 go
done
# now this is all garbled up
for i in 1 2 3 4 5 6 7 8
do
bash $0 go &
done
for i in 1 2 3 4 5 6 7 8; do wait; done
# using cat inbetween does not make it better
for i in 1 2 3 4 5 6 7 8
do
bash $0 go | cat &
done
for i in 1 2 3 4 5 6 7 8; do wait; done
# it does not help to use stdbuff after the thing that just printfs sporadicall
for i in 1 2 3 4 5 6 7 8
do
bash $0 go | stdbuf -oL cat &
done
for i in 1 2 3 4 5 6 7 8; do wait; done
# it does not help to use stdbuff before either - or I am not understanding stdbuff
for i in 1 2 3 4 5 6 7 8
do
stdbuf -o10000 bash $0 go | stdbuf -oL cat &
echo
done
for i in 1 2 3 4 5 6 7 8; do wait; done
# can I read - yes - they are now fine again
for i in 1 2 3 4 5 6 7 8
do
bash $0 go | (
while read LINE
do
echo $LINE
done
) &
echo
done
for i in 1 2 3 4 5 6 7 8; do wait; done