Bash에서 두 개의 파이프 라인을 어떻게 비교할 수 있습니까?


143

당신은 어떻게 할 수 diff를 강타에 임시 파일을 사용하지 않고 두 개의 파이프 라인을? 두 개의 명령 파이프 라인이 있다고 가정 해보십시오.

foo | bar
baz | quux

그리고 당신 diff은 그들의 출력에서 찾고 싶습니다 . 한 가지 해결책은 분명히 다음과 같습니다.

foo | bar > /tmp/a
baz | quux > /tmp/b
diff /tmp/a /tmp/b

Bash에서 임시 파일을 사용하지 않고도 그렇게 할 수 있습니까? 파이프 라인 중 하나를 파이프로 분할하여 하나의 임시 파일을 제거 할 수 있습니다.

foo | bar > /tmp/a
baz | quux | diff /tmp/a -

그러나 두 파이프 라인을 동시에 diff로 파이프 할 수는 없습니다 (적어도 명백한 방식은 아님). /dev/fd임시 파일을 사용하지 않고이를 수행 하는 데 영리한 트릭 이 있습니까?

답변:


146

2 개의 tmp 파일이있는 한 줄 (원하는 것이 아님)은 다음과 같습니다.

 foo | bar > file1.txt && baz | quux > file2.txt && diff file1.txt file2.txt

bash 사용하면 다음을 시도 할 수 있습니다.

 diff <(foo | bar) <(baz | quux)

 foo | bar | diff - <(baz | quux)  # or only use process substitution once

제 2 버전은 더 명확하게 보여줌으로써, 어떤을이었다 입력 당신을 생각 나게 할 것이다
-- /dev/stdin++ /dev/fd/63대신에 두 개의 번호 FDS의, 또는 무언가를.


명명 된 파이프조차도 적어도 bash가 파일 이름을 사용하여 프로세스 대체를 구현할 수있는 OS에서 bash가 /dev/fd/63설정 한 이미 열려있는 파일 설명자에서 명령을 열고 읽을 수있는 파일 이름을 얻는 것과 같은 파일 시스템에 나타나지 않습니다. 명령을 실행하기 전에 (즉, bash는 pipe(2)fork 이전에 사용 하고 fd 63 dup2에서에 대한 출력을 quux입력 파일 설명 diff자로 리디렉션합니다 .)

"매직" /dev/fd또는 시스템이없는 시스템 /proc/self/fd에서 bash는 명명 된 파이프를 사용하여 프로세스 대체를 구현할 수 있지만 임시 파일과 달리 적어도 자체적으로 관리해야하며 데이터는 파일 시스템에 기록되지 않습니다.

bash가 프로세스 대체를 구현 echo <(true)하여 파일 이름을 읽지 않고 인쇄하는 방법을 확인할 수 있습니다 . /dev/fd/63일반적인 Linux 시스템에서 인쇄 합니다. 또는 bash가 사용하는 시스템 호출에 대한 자세한 내용은 Linux 시스템에서이 명령이 파일 및 파일 설명자 시스템 호출을 추적합니다.

strace -f -efile,desc,clone,execve bash -c '/bin/true | diff -u - <(/bin/true)'

bash가 없으면 명명 된 파이프를 만들 수 있습니다. STDIN에서 하나의 입력을 읽고 다른 이름으로 명명 된 파이프를 사용 -하도록 지시하는 데 사용하십시오 diff.

mkfifo file1_pipe.txt
foo|bar > file1_pipe.txt && baz | quux | diff file1_pipe.txt - && rm file1_pipe.txt

참고로 할 수 있습니다 만 파이프를 하나 개의 출력여러 개의 입력 티 명령 :

ls *.txt | tee /dev/tty txtlist.txt 

위의 명령은 ls * .txt의 출력을 터미널에 표시하고 텍스트 파일 txtlist.txt에 출력합니다.

그러나 프로세스 대체를 사용 tee하면 동일한 데이터를 여러 파이프 라인에 공급할 수 있습니다 .

cat *.txt | tee >(foo | bar > result1.txt)  >(baz | quux > result2.txt) | foobar

5
bash 없이도 임시 fifo를 사용할 수 있습니다mkfifo a; cmd >a& cmd2|diff a -; rm a
unhammer

args 중 하나에 일반 파이프를 사용할 수 있습니다 pipeline1 | diff -u - <(pipeline2). 그러면 출력은 두 개의 숫자로 된 fd 대신에 -- /dev/stdinvs. ++ /dev/fd/67또는 어떤 것을 보여줌으로써 어느 입력이 어느 것인지를 더 명확하게 알려줍니다 .
Peter Cordes

프로세스 대체 ( foo <( pipe ))는 파일 시스템을 수정하지 않습니다. 파이프는 익명입니다 . 파일 시스템에 이름이 없습니다 . 쉘은 pipe시스템 호출을 사용하여 생성하지 않습니다 mkfifo. strace -f -efile,desc,clone,execve bash -c '/bin/true | diff -u - <(/bin/true)'직접보고 싶은 경우 파일 및 파일 디스크립터 시스템 호출을 추적하는 데 사용 하십시오. Linux /dev/fd/63에서 /proc가상 파일 시스템의 일부입니다 . 모든 파일 디스크립터에 대한 항목이 자동으로 있으며 컨텐츠의 사본이 아닙니다. foo 3<bar.txt계산 하지 않으면 "임시 파일"이라고 부를 수 없습니다
Peter Cordes

@PeterCordes 좋은 지적. 가시성을 높이기 위해 귀하의 의견을 답변에 포함 시켰습니다.
VonC

1
@PeterCordes 나는 당신에게 편집을 남겨 둘 것입니다 : 그것이 스택 오버플로를 흥미롭게 만드는 것입니다.
VonC

127

bash에서는 파이프 라인을 괄호로 묶어 서브 쉘을 사용하여 명령 파이프 라인을 개별적으로 실행할 수 있습니다. 그런 다음이 접두사에 <를 붙여 익명의 명명 된 파이프를 만든 다음 diff로 전달할 수 있습니다.

예를 들면 다음과 같습니다.

diff <(foo | bar) <(baz | quux)

익명의 명명 된 파이프는 bash에 의해 관리되므로 임시 파일과 달리 자동으로 생성 및 삭제됩니다.


1
동일한 솔루션 (익명 배치)에 대한 내 편집보다 훨씬 자세한 내용입니다. +1
VonC

4
이것을 Bash에서 프로세스 대체 라고 합니다 .
Franklin Yu

5

이 페이지에 도착 어떤 사람들은있는 라인 별 DIFF, 찾고있을 수도 comm또는이 grep -f대신 사용되어야한다.

주목해야 할 것은 모든 대답의 예에서 diff가 두 스트림이 모두 완료 될 때까지 실제로 시작되지 않는다는 것입니다. 예를 들어 다음과 같이 테스트하십시오.

comm -23 <(seq 100 | sort) <(seq 10 20 && sleep 5 && seq 20 30 | sort)

이것이 문제라면, sd (stream diff)를 시도 할 수 있습니다 comm. 위와 같은 정렬 이나 프로세스 대체가 필요하지 않은 sd (stream diff) 는 순서 또는 크기가 더 빠르며 grep -f 무한 스트림을 지원합니다.

내가 제안하는 테스트 예제는 다음과 같이 작성됩니다 sd.

seq 100 | sd 'seq 10 20 && sleep 5 && seq 20 30'

그러나 차이점은 바로 차이가 있다는 seq 100seq 10입니다. 스트림 중 하나가이면 tail -f프로세스 대체로 diff를 수행 할 수 없습니다.

다음 은 터미널에서 스트림을 확산시키는 방법에 대한 블로그 게시물sd 입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.