모두 두 프로그램 사이에 단방향 파이프를 만드는 방법을 알고 있습니다 ( stdout
첫 번째와 stdin
두 번째 프로그램의 바인딩 ) first | second
.
그러나 양방향 파이프, 즉 교차 결합 stdin
및 stdout
두 프로그램 을 만드는 방법은 무엇입니까? 껍질에 쉬운 방법이 있습니까?
모두 두 프로그램 사이에 단방향 파이프를 만드는 방법을 알고 있습니다 ( stdout
첫 번째와 stdin
두 번째 프로그램의 바인딩 ) first | second
.
그러나 양방향 파이프, 즉 교차 결합 stdin
및 stdout
두 프로그램 을 만드는 방법은 무엇입니까? 껍질에 쉬운 방법이 있습니까?
답변:
시스템의 파이프가 양방향 인 경우 (최소한 Linux는 아니지만 Solaris 11 및 일부 BSD에 있음) :
cmd1 <&1 | cmd2 >&0
교착 상태에주의하십시오.
또한 일부 시스템에서 일부 ksh93 버전은 소켓 쌍을|
사용하여 파이프 ( )를 구현 합니다. 소켓 쌍은 양방향이지만 ksh93은 역방향을 명시 적으로 종료하므로 위의 명령은 파이프 ( 시스템 호출 로 작성된 )가 양방향 인 시스템 에서도 해당 ksh93과 작동하지 않습니다 .pipe(2)
명명 된 파이프 ( 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
동일하고, 더 짧고, 더 휴대 가능합니다.
prog1 < fifo | prog2 > fifo
합니다.
prog1 < fifo | tee /dev/stderr | prog2 | tee /dev/stderr > fifo
.
prog2 < fifo0 > fifo1
, 당신과 당신의 작은 춤을 피할 수 있습니다 exec 30< ...
(방법에 의해서만 작동 bash
또는 yash
같은 10 FDS를 위해 참조).
dash
괜찮아 보이지만 (약간 다르게 동작합니다)
이것이 당신이하려는 일인지 확실하지 않습니다.
nc -l -p 8096 -c second &
nc -c first 127.0.0.1 8096 &
이것은 포트 8096에서 청취 소켓을 여는 것으로 시작하며 일단 연결이 설정되면 스트림 출력 및 스트림 입력으로 프로그램 second
을 생성합니다 .stdin
stdout
그런 다음 nc
청취 포트에 연결 하고 스트림 입력 으로, 스트림 출력으로 프로그램 first
을 생성 하는 두 번째 가 시작됩니다 .stdout
stdin
이것은 파이프를 사용하여 정확하게 수행되지는 않지만 필요한 것을 수행하는 것 같습니다.
이것은 네트워크를 사용하므로 2 대의 원격 컴퓨터에서 수행 할 수 있습니다. 이것은 거의 웹 서버 ( second
)와 웹 브라우저 ( first
)가 작동하는 방식입니다.
nc -U
UNIX 도메인 소켓을위한 전용 파일 시스템 주소 공간을 가지고있다.
pipexec 을 사용할 수 있습니다 :
$ pipexec -- '[A' cmd1 ] '[B' cmd2 ] '{A:1>B:0}' '{B:1>A:0}'
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
그렇습니다.
이러한 양방향 파이프를 작성하기위한 편리한 빌딩 블록은 현재 프로세스의 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 /를 사용하는 리눅스 고유의 구현이 가능하지만이 정도면 충분하다고 생각합니다.
( : <$FIFO & )
을 사용할 수있을 것입니다 . 게시 해 주셔서 감사합니다.
mktemp
있습니까? 나는 그것을 광범위하게 사용하고 새로운 도구가 그 자리를 차지하면 사용하기 시작하고 싶습니다.
도 있습니다
dpipe
vde2 패키지에 포함되어 있고 현재 배포 패키지 관리 시스템에 포함 된 "양방향 파이프" 입니다.
dpipe processA = processB
socat , 모든 것을 모든 것에 연결하는 도구.
socat EXEC:Program1 EXEC:Program2
@ StéphaneChazelas가 주석에 올바르게 언급했듯이 위의 예제는 "기본 형식"이며 유사한 질문에 대한 그의 답변 옵션 이있는 멋진 예제가 있습니다 .
socat
으로 파이프 대신 소켓을 사용합니다 (로 변경 가능 commtype=pipes
). nofork
파이프 / 소켓 사이에 데이터를 넣는 추가 socat 프로세스를 피하기 위해 옵션 을 추가 할 수 있습니다 . ( 내 답변 btw 에서 편집 주셔서 감사합니다 )
여기에는 많은 훌륭한 답변이 있습니다. 그래서 나는 그들과 함께 쉽게 놀 수있는 것을 추가하고 싶습니다. 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
...