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


11

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

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


출력을 어떻게 혼합하고 싶습니까? 분명히 "인수 순서"보다는 "수면 순서"를 원하기 때문에 출력을 혼합하려고합니다. 출력을 혼합해야하지만 라인을 혼합하지 않는 것, 즉 각 라인을 원자 적으로 인쇄해야합니까?
Gilles 'SO- 악한 중지

선으로. 시작된 모든 프로그램의 모든 라인은 조기에 제공되어야하지만 각 라인 내부에서 혼합되지 않아야합니다.
Vi.

이 작업을 수행하는 표준 방법은 다음과 같습니다 syslog.
Shadur

syslog로그를 사용 하지 않고 사용자 정의가 OK로 간주됩니까?
Vi.

이것은 지금까지 게시 된 다른 제안보다 이상적이지는 않지만 -s꼬리 옵션을 언급 할 가치가 있다고 생각했습니다 . 예를 들어 tail -f -s .1 file루프 지연을 기본 1 초에서 0.1 초로 줄입니다.
cpugeniusmv

답변:


4

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

1

잠금을 구현하는 솔루션 :

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

파일 시스템을 사용하는 것보다 잠금을 만드는 더 빠른 방법이 있어야합니다.


0

나는 당신이 당신의 라인이 너무 길면 하나의 프로그램이 가능하기 전에 대기 상태로 보내져 stdout에 라인을 작성하는 것을 도울 수있는 간단한 것을 생각할 수 없습니다.

그러나 프로세스 전환 전에 라인을 완전히 쓸 수있을 정도로 짧고 한 라인을 생성하는 데 시간이 오래 걸리면 read를 사용하여 출력을 버퍼링 할 수 있습니다.

예 :

((./script1 | while read line1; do echo $line1; done) & \
(./script2 | while read line2; do echo $line2; done)) | doSomethingWithOutput

아름답 지 않습니다. 그처럼 믿을 수 없을 것입니다. 아마도 그 성능은 좋을 것입니다.
Vi.

네가 옳아. 아름답지는 않지만 더 더러운 핵처럼 보입니다. 그러나 성능과 안정성을 판단하기에는 충분하지 않습니다. 또한 '표준 도구'를 사용하려고했습니다. 그래서 당신은 (결국) 어떤 추악함을 받아 들여야한다면 놀라지 않을 것입니다. 그러나 누군가가 더 만족스러운 해결책을 가지고있을 수도 있습니다.
xwst

현재 리포지토리에서 사용할 수 없으므로 약간의 "표준"으로 간주 될 수 없다는 점을 제외하고는 내 프로그램 (문제에 링크되어 있음)에 만족합니다. 해결책은 거기에 그것을 밀어 넣으려고 시도하는 것입니다 ...
Vi.

0

을 사용하여 명명 된 파이프를 만들고 mkfifo모든 출력을 명명 된 파이프에 덤프하고 수집 된 데이터에 대해 명명 된 파이프에서 별도로 읽을 수 있습니다.

mkfifo /tmp/mypipe
job1 > /tmp/mypipe &
job2 > /tmp/mypipe &
job3 > /tmp/mypipe &

cat /tmp/mypipe > /path/to/final_output &

wait; wait; wait; wait

1
는 어떻게이 보호 할 때 맹 글링에서 job1job2출력 긴 (> 4096 바이트) 라인을? 이것은 quesion에서 코드 첫 번째 예제와 동등한 파이프로 명명 된 것 같습니다.
Vi.

매우 공정한 포인트입니다. 귀하의 질문에 명시 적으로 불려지더라도 대량 생산물을 고려하지 않았습니다. 나는 이제 tee당신이 원하는 것과 똑같이 들리는 의 도구를 가지고 있지 않은지 궁금합니다 . syslog여러 로깅 도구 의 내부 또는 다른 로깅 도구는 여러 위치의 출력을 하나의 로그 파일로 확실히 집계하기 때문에 내부를 살펴보십시오 . @emmanual이 제안한 것처럼 잠금도 정답 일 수 있습니다.
DopeGhoti

0

오래된 질문, 나는 알고 있지만, 나는 이것에 대해 궁금해하고 있으며 이것이 내가 생각해 낸 것입니다.

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

1
페이지 크기 (일반적으로 4-8K)보다 큰 행으로 테스트해야합니다. 참조 mywiki.wooledge.org/...를
올레 단게
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.