두 프로그램 사이에 양방향 파이프를 만드는 방법은 무엇입니까?


63

모두 두 프로그램 사이에 단방향 파이프를 만드는 방법을 알고 있습니다 ( stdout첫 번째와 stdin두 번째 프로그램의 바인딩 ) first | second.

그러나 양방향 파이프, 즉 교차 결합 stdinstdout두 프로그램 을 만드는 방법은 무엇입니까? 껍질에 쉬운 방법이 있습니까?

shell  pipe 

답변:


30

시스템의 파이프가 양방향 인 경우 (최소한 Linux는 아니지만 Solaris 11 및 일부 BSD에 있음) :

cmd1 <&1 | cmd2 >&0

교착 상태에주의하십시오.

또한 일부 시스템에서 일부 ksh93 버전은 소켓 쌍을| 사용하여 파이프 ( )를 구현 합니다. 소켓 쌍은 양방향이지만 ksh93은 역방향을 명시 적으로 종료하므로 위의 명령은 파이프 ( 시스템 호출 로 작성된 )가 양방향 인 시스템 에서도 해당 ksh93과 작동하지 않습니다 .pipe(2)


1
아무도 이것이 리눅스에서도 작동하는지 알고 있습니까? (여기에서 archlinux 사용)
heinrich5991


41

명명 된 파이프 ( mkfifo)를 사용하면 상당히 "쉽습니다" . 프로그램이이를 위해 설계되지 않으면 교착 상태가 발생할 수 있기 때문에 따옴표로 쉽게 인용 할 수 있습니다.

mkfifo fifo0 fifo1
( prog1 > fifo0 < fifo1 ) &
( prog2 > fifo1 < fifo0 ) &
( exec 30<fifo0 31<fifo1 )      # write can't open until there is a reader
                                # and vice versa if we did it the other way

일반적으로 stdout 작성과 관련된 버퍼링이 있습니다. 예를 들어 두 프로그램이 모두

#!/usr/bin/perl
use 5.010;
say 1;
print while (<>);

무한 루프가 예상됩니다. 그러나 대신, 둘 다 교착 상태에 빠졌습니다. $| = 1출력 버퍼링을 해제 하려면 추가 (또는 이와 동등한) 가 필요합니다 . 교착 상태는 프로그램이 stdin에서 무언가를 기다리고 있기 때문에 발생 하지만 다른 프로그램의 stdout 버퍼에 앉아 아직 파이프에 기록되지 않았기 때문에 보이지 않습니다.

업데이트 : Stéphane Charzelas 및 Joost의 제안 사항 통합 :

mkfifo fifo0 fifo1
prog1 > fifo0 < fifo1 &
prog2 < fifo0 > fifo1

동일하고, 더 짧고, 더 휴대 가능합니다.


23
명명 된 파이프 하나만 있으면 충분 prog1 < fifo | prog2 > fifo합니다.
Andrey Vihrov

2
사실 @AndreyVihrov는 익명 파이프를 명명 된 파이프 중 하나로 대체 할 수 있습니다. 그러나 나는 대칭을 좋아한다
:-P

3
@ user14284 : Linux에서는 아마도 다음과 같이 할 수 있습니다 prog1 < fifo | tee /dev/stderr | prog2 | tee /dev/stderr > fifo.
Andrey Vihrov 10

3
당신이 그것을 할 경우 prog2 < fifo0 > fifo1, 당신과 당신의 작은 춤을 피할 수 있습니다 exec 30< ...(방법에 의해서만 작동 bash또는 yash같은 10 FDS를 위해 참조).
Stéphane Chazelas

1
@Joost Hmmm, 적어도 bash에는 필요가 없다는 것이 맞습니다. 쉘이 경로 재 지정 (파이프 열기 포함)을 수행하기 때문에 피포를 열기 전에 적어도 bash 포크를 막을 수 있습니다. dash괜찮아 보이지만 (약간 다르게 동작합니다)
derobert

13

이것이 당신이하려는 일인지 확실하지 않습니다.

nc -l -p 8096 -c second &
nc -c first 127.0.0.1 8096 &

이것은 포트 8096에서 청취 소켓을 여는 것으로 시작하며 일단 연결이 설정되면 스트림 출력 및 스트림 입력으로 프로그램 second을 생성합니다 .stdinstdout

그런 다음 nc청취 포트에 연결 하고 스트림 입력 으로, 스트림 출력으로 프로그램 first을 생성 하는 두 번째 가 시작됩니다 .stdoutstdin

이것은 파이프를 사용하여 정확하게 수행되지는 않지만 필요한 것을 수행하는 것 같습니다.

이것은 네트워크를 사용하므로 2 대의 원격 컴퓨터에서 수행 할 수 있습니다. 이것은 거의 웹 서버 ( second)와 웹 브라우저 ( first)가 작동하는 방식입니다.


1
그리고 nc -UUNIX 도메인 소켓을위한 전용 파일 시스템 주소 공간을 가지고있다.
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Linux에서는 -c 옵션을 사용할 수 없습니다. 내 행복은 단명 한 사람이었습니다 :-(
pablochacin


6

bash버전 4에는 명명 된 파이프없이 coproc순수하게 수행 할 수있는 명령이 있습니다 bash.

coproc cmd1
eval "exec cmd2 <&${COPROC[0]} >&${COPROC[1]}"

다른 껍질도 가능 coproc합니다.

아래는 더 자세한 답변이지만 두 가지가 아닌 세 가지 명령을 연결하므로 조금 더 흥미 롭습니다.

당신이 사용 cat하고 stdbuf기쁘면 구성을 이해하기 쉽게 만들 수 있다면.

버전 사용 bash으로 cat하고 stdbuf, 쉽게 이해하기 :

# start pipeline
coproc {
    cmd1 | cmd2 | cmd3
}
# create command to reconnect STDOUT `cmd3` to STDIN of `cmd1`
endcmd="exec stdbuf -i0 -o0 /bin/cat <&${COPROC[0]} >&${COPROC[1]}"
# eval the command.
eval "${endcmd}"

bash 4.2.25 버전에서는 <& $ var의 변수 확장이 불법이므로 eval을 사용해야합니다.

pure을 사용하는 버전 bash: 두 부분으로 나누고 coproc에서 첫 번째 파이프 라인을 시작한 다음 두 번째 부분 (단일 명령 또는 파이프 라인)을 첫 번째에 다시 연결하십시오.

coproc {
    cmd 1 | cmd2
}
endcmd="exec cmd3 <&${COPROC[0]} >&${COPROC[1]}"
eval "${endcmd}"

개념의 증거:

file ./prog, 라인을 소비하고 태그를 지정하고 다시 인쇄하는 더미 프로그램입니다. 버퍼링 문제를 피하기 위해 서브 쉘을 사용하는 것은 과잉 일 수 있습니다. 여기서 중요한 것은 아닙니다.

#!/bin/bash
let c=0
sleep 2

[ "$1" == "1" ] && ( echo start )

while : ; do
  line=$( head -1 )
  echo "$1:${c} ${line}" 1>&2
  sleep 2
  ( echo "$1:${c} ${line}" )
  let c++
  [ $c -eq 3 ] && exit
done

파일 ./start_cat 이 사용 버전입니다 bash, cat그리고stdbuf

#!/bin/bash

echo starting first cmd>&2

coproc {
  stdbuf -i0 -o0 ./prog 1 \
    | stdbuf -i0 -o0 ./prog 2 \
    | stdbuf -i0 -o0 ./prog 3
}

echo "Delaying remainer" 1>&2
sleep 5
cmd="exec stdbuf -i0 -o0 /bin/cat <&${COPROC[0]} >&${COPROC[1]}"

echo "Running: ${cmd}" >&2
eval "${cmd}"

또는 file ./start_part. 이것은 순수 bash만을 사용하는 버전 입니다. 데모 목적을 위해 stdbuf실제 프로그램은 버퍼링으로 인한 차단을 피하기 위해 내부 버퍼링을 처리해야하기 때문에 여전히 사용 하고 있습니다.

#!/bin/bash

echo starting first cmd>&2

coproc {
  stdbuf -i0 -o0 ./prog 1 \
    | stdbuf -i0 -o0 ./prog 2
}

echo "Delaying remainer" 1>&2
sleep 5
cmd="exec stdbuf -i0 -o0 ./prog 3 <&${COPROC[0]} >&${COPROC[1]}"

echo "Running: ${cmd}" >&2
eval "${cmd}"

산출:

> ~/iolooptest$ ./start_part
starting first cmd
Delaying remainer
2:0 start
Running: exec stdbuf -i0 -o0 ./prog 3 <&63 >&60
3:0 2:0 start
1:0 3:0 2:0 start
2:1 1:0 3:0 2:0 start
3:1 2:1 1:0 3:0 2:0 start
1:1 3:1 2:1 1:0 3:0 2:0 start
2:2 1:1 3:1 2:1 1:0 3:0 2:0 start
3:2 2:2 1:1 3:1 2:1 1:0 3:0 2:0 start
1:2 3:2 2:2 1:1 3:1 2:1 1:0 3:0 2:0 start

그렇습니다.


5

이러한 양방향 파이프를 작성하기위한 편리한 빌딩 블록은 현재 프로세스의 stdout과 stdin을 함께 연결하는 것입니다. 이것을 ioloop라고하자. 이 함수를 호출 한 후에는 일반 파이프 만 시작하면됩니다.

ioloop &&     # stdout -> stdin 
cmd1 | cmd2   # stdin -> cmd1 -> cmd2 -> stdout (-> back to stdin)

최상위 쉘의 디스크립터를 수정하지 않으려면 서브 쉘에서이를 실행하십시오.

( ioloop && cmd1 | cmd2 )

명명 된 파이프를 사용한 ioloop의 이식 가능한 구현은 다음과 같습니다.

ioloop() {
    FIFO=$(mktemp -u /tmp/ioloop_$$_XXXXXX ) &&
    trap "rm -f $FIFO" EXIT &&
    mkfifo $FIFO &&
    ( : <$FIFO & ) &&    # avoid deadlock on opening pipe
    exec >$FIFO <$FIFO
}

명명 된 파이프는 파일 시스템에 ioloop 설정 중에 잠깐 동안 만 존재합니다. mktemp는 더 이상 사용되지 않으며 잠재적으로 경쟁 공격에 취약하기 때문에이 기능은 POSIX가 아닙니다.

명명 된 파이프를 필요로하지 않는 / proc /를 사용하는 리눅스 고유의 구현이 가능하지만이 정도면 충분하다고 생각합니다.


재미있는 기능, +1. 아마도 문장이나 2 개의 추가 설명 ( : <$FIFO & )을 사용할 수있을 것입니다 . 게시 해 주셔서 감사합니다.
Alex Stragies

나는 조금 둘러보고 빈 채로 왔습니다; 지원 중단에 대한 정보는 어디서 찾을 수 mktemp있습니까? 나는 그것을 광범위하게 사용하고 새로운 도구가 그 자리를 차지하면 사용하기 시작하고 싶습니다.
DopeGhoti

Alex : Naned 파이프의 open (2) syscall이 차단되고 있습니다. "exec <$ PIPE> $ PIPE"를 시도하면 다른 프로세스가 다른 쪽을 열기를 기다리는 동안 중단됩니다. ": <$ FIFO &"명령은 백그라운드의 서브 쉘에서 실행되며 양방향 재 지정이 성공적으로 완료되도록합니다.
user873942

DopeGhoti : mktemp (3) C 라이브러리 함수는 더 이상 사용되지 않습니다. mktemp (1) 유틸리티가 아닙니다.
user873942

4

도 있습니다

  • dpipevde2 패키지에 포함되어 있고 현재 배포 패키지 관리 시스템에 포함 된 "양방향 파이프" 입니다.

    dpipe processA = processB

  • socat , 모든 것을 모든 것에 연결하는 도구.

    socat EXEC:Program1 EXEC:Program2

@ StéphaneChazelas가 주석에 올바르게 언급했듯이 위의 예제는 "기본 형식"이며 유사한 질문에 대한 그의 답변 옵션 이있는 멋진 예제가 있습니다 .


기본적 socat으로 파이프 대신 소켓을 사용합니다 (로 변경 가능 commtype=pipes). nofork파이프 / 소켓 사이에 데이터를 넣는 추가 socat 프로세스를 피하기 위해 옵션 을 추가 할 수 있습니다 . ( 내 답변 btw 에서 편집 주셔서 감사합니다 )
Stéphane Chazelas

0

여기에는 많은 훌륭한 답변이 있습니다. 그래서 나는 그들과 함께 쉽게 놀 수있는 것을 추가하고 싶습니다. stderr어디에서나 리디렉션되지 않는다고 가정 합니다. 두 개의 스크립트를 작성하십시오 (예 : a.sh 및 b.sh).

#!/bin/bash
echo "foo" # change to 'bar' in second file

for i in {1..10}; do
  read input
  echo ${input}
  echo ${i} ${0} got: ${input} >&2
done

그런 다음 좋은 방법으로 연결하면 콘솔에 표시됩니다.

1 ./a.sh got: bar
1 ./b.sh got: foo
2 ./a.sh got: foo
2 ./b.sh got: bar
3 ./a.sh got: bar
3 ./b.sh got: foo
4 ./a.sh got: foo
4 ./b.sh got: bar
...
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.