누군가 사용 방법에 대한 몇 가지 예를 제공할 수 있습니까?coproc
?
답변1
공동 처리는 ksh
기능입니다(이미 개발 중 ksh88
). 이 기능은 초기(90년대 초반)부터 존재했으며 zsh
2009년에야 추가되었습니다.bash
4.0
그러나 3개의 셸 간의 동작과 인터페이스에는 상당한 차이가 있습니다.
하지만 아이디어는 동일합니다. 즉, 명명된 파이프를 사용하지 않고도 백그라운드에서 작업을 시작하고 입력을 보내고 출력을 읽을 수 있다는 것입니다.
이는 일부 시스템에서 최신 버전의 ksh93에 대한 대부분의 쉘 및 소켓 쌍에 대해 명명되지 않은 파이프를 사용하여 수행됩니다.
에서는 데이터를 공급 a | cmd | b
하고 출력을 읽습니다 . 코프로세스로 실행하면 쉘 이 .a
cmd
b
cmd
a
b
ksh 공동 처리
에서는 ksh
보조 프로세스를 시작합니다.
cmd |&
cmd
다음을 수행하여 데이터를 제공할 수 있습니다.
echo test >&p
또는
print -p test
cmd
다음을 사용하여 출력을 읽습니다 .
read var <&p
또는
read -p var
cmd
fg
백그라운드 작업으로 실행되면 , , 을(를) 사용하고 을 bg
(를) 통해 kill
참조 할 수 있습니다.%job-number
$!
cmd
읽고 있는 파이프의 쓰기 끝을 닫으려면 다음을 수행할 수 있습니다.
exec 3>&p 3>&-
cmd
그리고 ( 쓰기 중인) 다른 파이프의 읽기 끝을 닫습니다 .
exec 3<&p 3<&-
파이프 파일 설명자를 먼저 다른 fd에 저장하지 않으면 두 번째 coprocess를 시작할 방법이 없습니다. 예를 들어:
tr a b |&
exec 3>&p 4<&p
tr b c |&
echo aaa >&3
echo bbb >&p
zsh 공동 프로세스
에서 zsh
보조 프로세스는 에서와 거의 동일합니다 ksh
. 유일한 실제 차이점은 공동 프로세스가 키워드 zsh
로 coproc
시작 된다는 것입니다.
coproc cmd
echo test >&p
read var <&p
print -p test
read -p var
행위:
exec 3>&p
참고: 이는 coproc
파일 설명자를 fd 3
( 에서와 같이 ksh
)로 이동하는 것이 아니라 복사합니다. 따라서 피드를 닫거나 파이프를 읽는 명확한 방법이 없습니다. 다른 시작다른 coproc
.
예를 들어 피드 끝을 닫으면 다음과 같습니다.
coproc tr a b
echo aaaa >&p # send some data
exec 4<&p # preserve the reading end on fd 4
coproc : # start a new short-lived coproc (runs the null command)
cat <&4 # read the output of the first coproc
zsh
파이프 기반 공동 프로세스 (2000년에 출시된 3.1.6-dev19 기준) 외에도 의사 tty 기반 구성이 있습니다. 예를 들어 expect
대부분의 프로그램과 상호 작용하기 위한 ksh 스타일 공동 프로세스는 작동하지 않습니다. 출력이 파이프될 때 버퍼링을 시작합니다.
여기 몇 가지 예가 있어요.
협업 프로세스 시작 x
:
zmodload zsh/zpty
zpty x cmd
(여기에 cmd
간단한 명령이 있습니다. 그러나 eval
or 함수를 사용하면 더 멋진 작업을 수행할 수 있습니다.)
공동 처리를 위한 피드 데이터:
zpty -w x some data
공동 처리된 데이터 읽기(가장 간단한 경우):
zpty -r x var
와 유사하게 expect
, 주어진 패턴과 일치하는 보조 프로세스의 특정 출력을 기다릴 수 있습니다.
bash 공동 프로세스
Bash 구문은 최근 ksh93, bash 및 zsh에 추가된 새로운 기능을 기반으로 크게 업데이트되었습니다. 이는 동적으로 할당된 10개 이상의 파일 설명자를 처리할 수 있는 구문을 제공합니다.
bash
공급기초적인 coproc
문법, 그리고확장하다하나.
기본 문법
코프로세스를 시작하는 기본 구문은 다음과 같습니다 zsh
.
coproc cmd
ksh
또는 에서 zsh
코루틴으로 들어오고 나가는 파이프는 >&p
및 를 통해 액세스할 수 있습니다 <&p
.
하지만 에서는 bash
coprocess의 파이프와 coprocess의 다른 파이프에 대한 파일 설명자가 배열 ( 및 $COPROC
각각 ) 로 반환됩니다 . 따라서...${COPROC[0]}
${COPROC[1]}
코프로세스에 데이터를 제공합니다.
echo xxx >&"${COPROC[1]}"
coprocess에서 데이터를 읽습니다.
read var <&"${COPROC[0]}"
기본 구문을 사용하면 한 번에 하나의 보조 프로세스만 시작할 수 있습니다.
확장 구문
확장 구문에서는 다음을 수행할 수 있습니다.이름공동 프로세스(예: zsh
zpty 공동 프로세스):
coproc mycoproc { cmd; }
주문하다가지다복합 명령이 됩니다. (위의 예가 어떻게 떠오르는지 주목하세요 function f { ...; }
.)
이번에는 파일 설명자가 ${mycoproc[0]}
및 에 있습니다 ${mycoproc[1]}
.
한 번에 여러 코루틴을 시작할 수 있지만하다보조 프로세스가 계속 실행되는 동안(비대화형 모드에서도) 보조 프로세스를 시작하면 경고가 표시됩니다.
확장된 구문을 사용하여 파일 설명자를 닫을 수 있습니다.
coproc tr { tr a b; }
echo aaa >&"${tr[1]}"
exec {tr[1]}>&-
cat <&"${tr[0]}"
이 종료는 4.3 이전의 bash 버전에서는 작동하지 않으므로 대신 작성해야 합니다.
fd=${tr[1]}
exec {fd}>&-
및 에서와 같이 ksh
이러한 zsh
파이프 파일 설명자는 close-on-exec로 표시됩니다.
그러나 에서 bash
실행된 명령에 전달하는 유일한 방법은 fds 0
또는 . 이는 단일 명령과 상호 작용할 수 있는 공동 프로세스의 수를 제한합니다. (아래 예를 참조하세요.)1
2
yash 프로세스 및 파이프라인 리디렉션
yash
자체적으로 공동 처리 기능은 없지만 동일한 개념을 구현하는 데 사용할 수 있습니다.관로그리고프로세스리디렉션 기능. yash
시스템 호출 인터페이스가 있으므로 pipe()
이런 종류의 작업을 비교적 쉽게 수동으로 수행할 수 있습니다.
다음을 통해 협업 프로세스를 시작할 수 있습니다.
exec 5>>|4 3>(cmd >&5 4<&- 5>&-) 5>&-
먼저 하나를 생성하고 pipe(4,5)
(5개 쓰기, 4개 읽기) fd 3을 파이프로 리디렉션합니다. 파이프의 다른 쪽 끝은 stdin으로 실행되고 stdout은 이전에 생성된 파이프로 이동합니다. 그런 다음 필요하지 않은 상위 파이프에서 파이프의 쓰기 끝을 닫습니다. 이제 쉘에서 fd 3을 cmd의 stdin에 연결하고 fd 4를 cmd의 stdout에 파이프합니다.
이러한 파일 설명자에는 close-on-exec 플래그가 설정되어 있지 않습니다.
피드 데이터:
echo data >&3 4<&-
데이터 읽기:
read var <&4 3>&-
평소대로 fds를 끌 수 있습니다:
exec 3>&- 4<&-
자, 왜 그렇게 인기가 없습니까?
명명된 파이프를 사용하는 것보다 이점이 거의 없습니다.
공동 프로세스는 표준 명명된 파이프를 통해 쉽게 구현할 수 있습니다. 명명된 파이프가 언제 도입되었는지는 정확히 모르지만 아마도 coprocesses가 출현한 이후였을 것입니다 (아마 80년대 중반, ksh88은 88년에 "출시"되었지만 몇 년 전 AT&T에서 내부적으로 사용되었다고 ksh
생각합니다 ) ksh
) 이유를 설명할 수 있습니다.
cmd |&
echo data >&p
read var <&p
다음과 같이 쓸 수 있습니다:
mkfifo in out
cmd <in >out &
exec 3> in 4< out
echo data >&3
read var <&4
특히 여러 공동 프로세스를 실행해야 하는 경우 이들과 상호 작용하는 것이 더 간단합니다. (아래 예를 참조하세요.)
사용의 유일한 이점 coproc
은 사용 후 명명된 파이프를 정리할 필요가 없다는 것입니다.
교착상태에 빠지기 쉽다
쉘은 일부 구조에서 파이프를 사용합니다.
- 쉘 튜브:
cmd1 | cmd2
, - 명령 대체:
$(cmd)
, - 그리고프로세스 교체:
<(cmd)
,>(cmd)
.
그 중 데이터가 유입되는 곳은단 하나서로 다른 프로세스 사이의 방향.
그러나 공동 프로세스와 명명된 파이프를 사용하면 교착 상태에 빠지기 쉽습니다. 파일 설명자가 열려 있는 것을 방지하고 프로세스를 활성 상태로 유지하려면 어떤 명령이 어떤 파일 설명자를 열었는지 추적해야 합니다. 교착 상태 조사는 발생 여부가 불확실할 수 있으므로 까다로울 수 있습니다. 예를 들어, 파이프를 채우기에 충분한 데이터가 전송되는 경우에만 가능합니다.
expect
효과가 생각보다 나쁩니다.
코프로세스의 주요 목적은 셸이 명령과 상호 작용할 수 있는 방법을 제공하는 것입니다. 그러나 그것은 잘 작동하지 않습니다.
위에서 언급한 교착 상태의 가장 간단한 형태는 다음과 같습니다.
tr a b |&
echo a >&p
read var<&p
출력이 터미널로 전송되지 않기 때문에 tr
출력이 버퍼링됩니다. 따라서 파일의 끝을 확인 stdin
하거나 출력할 버퍼가 가득 찬 데이터를 축적할 때까지 아무 것도 출력하지 않습니다. 따라서 위의 내용은 쉘이 출력 a\n
(2바이트만) 한 후 쉘이 더 많은 데이터를 보낼 때까지 기다리면서 read
무기한 차단됩니다 .tr
간단히 말해서 파이프는 명령과 상호 작용하는 데 적합하지 않습니다. 코프로세스는 출력을 버퍼링하지 않는 명령과 상호 작용하는 데에만 사용할 수 있습니다.또는stdbuf
예를 들어 최근 GNU 또는 FreeBSD 시스템에서 특정 명령을 사용하여 명령에 출력을 버퍼링하지 않도록 지시할 수 있습니다 .
그렇기 때문에 expect
대신 zpty
의사 터미널을 사용하십시오. expect
명령과 상호 작용하도록 설계된 도구이며 매우 잘 작동합니다.
파일 설명자 처리는 지루하고 올바르게 수행하기 어렵습니다.
공동 처리를 사용하면 단순한 쉘 및 튜브보다 더 복잡한 배관 작업을 완료할 수 있습니다.
기타 Unix.SE 답변coproc 사용법의 예가 있습니다.
다음은 간단한 예입니다.한 명령의 출력 복사본을 3개의 다른 명령에 공급한 다음 3개의 명령의 출력을 연결하는 함수가 필요하다고 상상해 보세요.
모두 파이프를 사용합니다.
예: printf '%s\n' foo bar
, tr a b
및 sed 's/./&&/g'
의 출력을 공급하여 cut -b2-
다음과 같은 결과를 얻습니다.
foo
bbr
ffoooo
bbaarr
oo
ar
우선, 이것이 반드시 명백하지는 않지만 교착 상태가 발생할 수 있으며 단 몇 킬로바이트의 데이터 후에 발생하기 시작합니다.
그러면 쉘에 따라 다양한 방법으로 해결해야 하는 다양한 문제가 발생하게 됩니다.
예를 들어 zsh
다음과 같이 할 수 있습니다.
f() (
coproc tr a b
exec {o1}<&p {i1}>&p
coproc sed 's/./&&/g' {i1}>&- {o1}<&-
exec {o2}<&p {i2}>&p
coproc cut -c2- {i1}>&- {o1}<&- {i2}>&- {o2}<&-
tee /dev/fd/$i1 /dev/fd/$i2 >&p {o1}<&- {o2}<&- &
exec cat /dev/fd/$o1 /dev/fd/$o2 - <&p {i1}>&- {i2}>&-
)
printf '%s\n' foo bar | f
위에서 coprocess fd는 close-on-exec 플래그를 설정하지만아니요그로부터 복사된 것(예 {o1}<&p
: ). 따라서 교착 상태를 방지하려면 교착 상태가 필요하지 않은 프로세스에서 교착 상태가 닫혀 있는지 확인해야 합니다.
다시 말하지만, 파이프를 열어두는 쉘 프로세스가 없는지 확인하기 위해 서브쉘을 사용하고 exec cat
마지막에 이를 사용해야 합니다.
ksh
(여기 ) 의 경우 ksh93
다음과 같아야 합니다.
f() (
tr a b |&
exec {o1}<&p {i1}>&p
sed 's/./&&/g' |&
exec {o2}<&p {i2}>&p
cut -c2- |&
exec {o3}<&p {i3}>&p
eval 'tee "/dev/fd/$i1" "/dev/fd/$i2"' >&"$i3" {i1}>&"$i1" {i2}>&"$i2" &
eval 'exec cat "/dev/fd/$o1" "/dev/fd/$o2" -' <&"$o3" {o1}<&"$o1" {o2}<&"$o2"
)
printf '%s\n' foo bar | f
(노트:ksh
이는 socketpairs
대신 을 사용하는 시스템에서는 작동하지 않으며 pipes
Linux /dev/fd/n
에서와 마찬가지로 작동합니다. )
위의 ksh
fd는 2
명령줄에서 명시적으로 전달되지 않는 한 close-on-exec 플래그로 표시됩니다. 이것이 바로 with처럼 사용하지 않는 파일 설명자를 닫을 필요가 없는 이유입니다 . 하지만 to 및 ... 의 새 값을 전달 zsh
해야 하는 이유이기도 합니다 .{i1}>&$i1
eval
$i1
tee
cat
bash
close-on-exec 플래그를 피할 수 없기 때문에 이는 불가능합니다.
위에서는 간단한 외부 명령만 사용했기 때문에 비교적 간단합니다. 그 안에 쉘 구성을 사용하려고 하면 상황이 더 복잡해지고 쉘 버그가 발생하기 시작합니다.
위의 내용을 명명된 파이프를 사용하는 것과 비교해 보세요.
f() {
mkfifo p{i,o}{1,2,3}
tr a b < pi1 > po1 &
sed 's/./&&/g' < pi2 > po2 &
cut -c2- < pi3 > po3 &
tee pi{1,2} > pi3 &
cat po{1,2,3}
rm -f p{i,o}{1,2,3}
}
printf '%s\n' foo bar | f
결론적으로
명령과 상호 작용하려면 , expect
또는 zsh
의 zpty
또는 명명된 파이프를 사용하세요.
파이프로 멋진 파이프 작업을 수행하려면 명명된 파이프를 사용하세요.
코프로세스는 위의 작업 중 일부를 수행할 수 있지만 사소하지 않은 작업에 대해 심각한 골치 아픈 작업을 수행할 준비가 되어 있습니다.
답변2
ksh88
코프로세스는 셸(1988년)과 함께 셸 스크립팅 언어에 처음 도입되었으며 나중에 zsh
1993년 이전에 도입되었습니다.
ksh에서 공동 프로세스를 시작하는 구문은 입니다 command |&
. 여기에서 를 사용하여 command
표준 입력 에 쓰고 print -p
를 사용하여 표준 출력을 읽을 수 있습니다 read -p
.
수십 년이 지나서 이 기능이 없었던 bash는 마침내 버전 4.0에서 이 기능을 도입했습니다. 불행하게도 호환되지 않고 더 복잡한 구문이 선택되었습니다.
Bash 4.0 이상에서는 다음 명령을 사용하여 코프로세스를 시작할 수 있습니다 coproc
. 예를 들면 다음과 같습니다.
$ coproc awk '{print $2;fflush();}'
그런 다음 다음과 같은 방법으로 stdin 명령에 무언가를 전달할 수 있습니다.
$ echo one two three >&${COPROC[1]}
그리고 다음을 사용하여 awk 출력을 읽습니다.
$ read -ru ${COPROC[0]} foo
$ echo $foo
two
ksh에서는 다음과 같습니다.
$ awk '{print $2;fflush();}' |&
$ print -p "one two three"
$ read -p foo
$ echo $foo
two
답변3
여기에 또 다른 좋은(그리고 작동하는) 예가 있습니다. 바로 BASH로 작성된 간단한 서버입니다. OpenBSD가 필요하며 netcat
클래식은 작동하지 않습니다. 물론 Unix 소켓 대신 inet 소켓을 사용할 수도 있습니다.
server.sh
:
#!/usr/bin/env bash
SOCKET=server.sock
PIDFILE=server.pid
(
exec </dev/null
exec >/dev/null
exec 2>/dev/null
coproc SERVER {
exec nc -l -k -U $SOCKET
}
echo $SERVER_PID > $PIDFILE
{
while read ; do
echo "pong $REPLY"
done
} <&${SERVER[0]} >&${SERVER[1]}
rm -f $PIDFILE
rm -f $SOCKET
) &
disown $!
client.sh
:
#!/usr/bin/env bash
SOCKET=server.sock
coproc CLIENT {
exec nc -U $SOCKET
}
{
echo "$@"
read
} <&${CLIENT[0]} >&${CLIENT[1]}
echo $REPLY
용법:
$ ./server.sh
$ ./client.sh ping
pong ping
$ ./client.sh 12345
pong 12345
$ kill $(cat server.pid)
$
답변4
"코루틴"이란 무엇입니까?
"co-process"의 줄임말로, 셸과 협력하는 두 번째 프로세스를 의미합니다. 표준 I/O가 동일한 표준 입력 및 출력을 공유하는 대신 특수 I/O를 통해 상위 셸에 연결된다는 점을 제외하면 명령 끝에 "&"로 시작하는 백그라운드 작업과 매우 유사합니다. 상위 쉘입니다. FIFO라고 불리는 파이프의 일종입니다. 참고로여기를 클릭하세요
zsh에서 coproc을 시작합니다.
coproc command
명령은 stdin에서 읽거나 stdout에 쓰도록 준비되어야 합니다. 그렇지 않으면 coproc으로 많이 사용되지 않습니다.
이 기사를 읽어보세요여기exec와 coproc 간의 사례 연구를 제공합니다.