여러 프로그램에서 인쇄 된 줄을 안전하게 결합하는 방법은 무엇입니까? 더 많은

여러 프로그램을 병렬로 실행하고 출력을 하나의 파이프에 결합하고 싶다고 가정 해보십시오.

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단점 없이 어떻게합니까?



답변

GNU Parallel.

에서 릴리스 노트 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


답변