IO 스트림에 대한 일반적인 분할+병합 구문 찾기

IO 스트림에 대한 일반적인 분할+병합 구문 찾기

필요한 작업을 자주 수행하고 싶습니다.

  1. stdout파이프라인(파이프라고 함)을 pipeline-before두 개의 병렬 스트림으로 분할합니다.
  2. 결과 스트림( stdin)을 두 개의 개별 파이프( pipeline-between-0pipeline-between-1)에 공급합니다.
  3. 두 개의 결과 stdout스트림 병합엄격한 순서로;
  4. 병합된 결과 스트림을 stdin다른 파이프( pipeline-after)에 공급합니다.

(3)에서 "엄격한 순서로"는 의 모든 출력이 의 pipeline-between-0출력보다 먼저 병합된 출력 스트림에 나타나야 함 을 의미합니다 pipeline-between-1.

모든 것을 그래픽으로 표현할 수 있습니다.

pipeline-before --.--- pipeline-between-0 -.
                   \                        \
                    `- pipeline-between-1 ---`-- pipeline-after

pipeline-between-0이러한/쌍의 예는 pipeline-between-1다음과 같습니다.

  1. head -n 1 | tr 'a-z' 'A-Z'
  2. tail -n +2 | sort -t $'\t' -k1,1

영어에서는 이 조합을 "첫 번째 행을 모두 대문자로 만들고 나머지 행을 첫 번째 열로 정렬하는 것"이라고 설명합니다.

묻다:그러한 작업을 표현하기 위한 일반적인 쉘 구문이 있습니까?

나는 이 질문에 대한 답변에 관심이 있습니다 zsh.bash


일반적으로 말하면 이것은 구문입니다.아니요일하다:

$ pipeline-before | tee >( pipeline-between-0 ) | pipeline-between-1 | pipeline-after

이 구문은 다음 두 가지 이유로 실패합니다.

  1. 의 일부 출력이 pipeline-between-1의 일부 출력 앞에 나타나는 경우가 많습니다 pipeline-between-0.
  2. SIGPIPE결국 출력이 잘립니다( 신호 때문인 것으로 의심됩니다 ).

나는 다음을 시도했다운동(나는 100% 이해하지 못한다는 것을 인정합니다):

{
  pipeline-before |
  { tee >( pipeline-between-0 4>&1 1>&3 ); } |
  pipeline-between-1
} 3>&1 | pipeline-after

AFAICT, 이 구문은 위에 나열된 첫 번째 문제를 해결하는 것으로 보입니다(즉, 일부 비공식 테스트를 기반으로 의 pipeline-between-0출력이 pipeline-between-1올바른 순서로 나타남). 그러나 불행히도 적어도 어떤 경우에는 최종 출력이 여전히 잘립니다.

답변1

어쩌면 명명된 파이프(FIFO)만 필요할 수도 있습니다.

아래 예:

 { seq 1 100000 | grep 1$ & seq 1 100000 | grep 2$ ; } > unsorted

파일에서 1과 2로 끝나는 숫자의 혼합이 반환되어야 합니다 unsorted. 우리는 그것들이 정렬되기를 원하므로(모든 숫자는 1로 끝나고 모든 숫자는 2로 끝남) 이제 각 결과에 하나씩 두 개의 명명된 파이프를 만들고 원하는 순서로 결합합니다.

mkfifo stream{1,2}
{ seq 1 100000 | grep 1$ >stream1 & seq 1 100000 | grep 2$ > stream2 ; } &\
cat stream1 stream2 > sorted

파일의 순서가 동일한지 확인하세요.

diff -q {un,}sorted 

(그들은 달라야 합니다) 정렬된 순서가 예상대로인지 확인합니다.

sed 1,10000q sorted | grep 2$

(결과가 없어야 하며 unsorted파일은 데이터를 반환해야 합니다)

bash명명된 파이프는 에서도 동일하게 작동해야 합니다 zsh.


또는 의사코드의 가장 일반적인 표현은 다음과 같습니다.

  1. tee입력 스트림("파이프 전")은 FIFO를 통해 여러 병렬 스트림으로 복사됩니다 in-i.
  2. 각 스트림은 서로 다른 명령에 의해 병렬로 처리되고 해당 출력은 스트림 out-i("i 사이의 파이프")으로 전송됩니다.
  3. 원하는 순서로 출력 스트림을 연결하고 다음 명령("애프터 파이프")으로 전달합니다.

mkfifo {in,out}-{0..n}

pre-cmd | tee in-0 in-1 ... in-n | cat >/dev/null &
cmd-0 <in-0 >out-0 &
cmd-1 <in-0 >out-0 &
....
cmd-i <in-n >out-n &
cat out-0 out-1 ... out-n | after-cmd

나는 일반화의 이유로 cat >/dev/null대신 사용하고 있습니다. 동일한 내용 은 (아마도) 궁극적으로 중복됩니다.cat >in-ncat

예:

mkfifo {in,out}-{0..2}
seq 0 100 | tee in-{0..2} | cat >/dev/null &
grep '33$' <in-0 >out-0 &
awk '$1<2' <in-1 >out-1 &
sed '/^.\{1,2\}$/d' <in-2 >out-2 &
cat out-2 out-0 out-1 | tr '\n' '-'

결과:100-33-0-1-

답변2

당신은 그것을 찾고 있습니까 parallel --tee? 출력을 위한 충분한 여유 디스크 공간이 있는 한 /tmp모든 크기의 입력을 쉽게 처리할 수 있습니다 .

(printf "Header1\tHeader2\n"; paste <(seq 20 -1 11) <(seq 10) ) |
  parallel -k --pipe --tee ::: "head -n  1 | tr 'a-z' 'A-Z'" "tail -n +2 | sort -t $'\t' -k1,1"

또는 bash 기능과 동일합니다.

pipeline-before() {
    printf "Header1\tHeader2\n"
    paste <(seq 20 -1 11) <(seq 10)
}
pipeline-between-0() {
    head -n  1 | tr 'a-z' 'A-Z'
}
pipeline-between-1() {
    tail -n +2 | sort -t $'\t' -k1,1
}
pipeline-after() {
    echo "This is pipeline-after"
    cat
    echo "Done"
}
export -f pipeline-before pipeline-between-0 pipeline-between-1 pipeline-after

pipeline-before |
  parallel -k --pipe --tee ::: pipeline-between-0 pipeline-between-1 |
  pipeline-after

그렇지 않다면 더 많은 입력, 출력 및 파이프라인-*이 무엇인지에 대한 예를 자세히 설명할 수 있습니까?

관련 정보