여러 프로세스 대체가 포함된 상당히 복잡한 명령이 포함된 스크립트가 있는데, 프로세스 대체 내의 명령에서 종료 코드를 가져와야 합니다. 명명된 파이프를 통해 이 작업을 수행하려고 하는데 이전에 명명된 파이프를 사용한 적이 없기 때문에 문제가 있습니다.
예를 들어, 다음 명령이 주어지면:
somecommand <(someothercommand; echo $? > named_pipe) <(someothercommand; echo $? > named_pipe) &
somecommand
완료될 때까지 기다리고 모든 종료 코드를 읽는 올바른 방법은 무엇입니까 named_pipe
(실제로 얼마나 많은 종료 코드가 작성될지 모른다고 가정 named_pipe
)? 지금까지의 시도는 차단 somecommand
(에서 무언가를 읽기를 기다리고 있다고 가정하기 때문에 named_pipe
)되거나 종료 코드만 읽는 결과를 가져왔습니다.
답변1
사실 이는 그리 간단해 보이지는 않습니다. 쉘이 프로세스 교체 대기를 지원한다면 가장 좋을 수도 있지만 Bash는 그렇게 하지 않을 것 같습니다.
또 다른 문제는 명명된 파이프가 거기에 쓰고 싶은 줄 수를 알 수 없다는 것입니다. 모든 작성기가 닫히면 파이프는 EOF를 읽지만 모든 쓰기에서 EOF를 얻을 수 있습니다 echo
. 동시에 공격하지 않는 한, 그런 경우에는 그렇지 않습니다.
그러나 처음부터 쓰기 fd를 열도록 프로세스 교체를 예약하여 모든 작업이 완료된 후 EOF가 한 번만 발생하도록 하는 것이 가능해 보입니다.
이와 같이 echo somecommand
및 를 사용하여 실제 명령을 true
나타냅니다 .false
#!/bin/bash
dir=$(mktemp -d)
p="$dir/p"
mkfifo "$p"
# whole subshell sent to the background
(exec 3> "$p";
# both process substitutions get a copy of fd 3
echo somecommand \
<(false; echo "cmd1: $?" >&3) \
<(true; echo "cmd2: $?" >&3) \
) &
# read the exit statuses, this will see EOF once all the three
# background processes above finish
cat "$p"
rm -rf "$dir" 2>/dev/null
파이프에 인쇄되는 라인의 순서는 시간에 따라 다르며 본질적으로 무작위입니다.
echo somecommand
또한 느리게 실행되는 경우 먼저 나오는 출력을 얻을 수 있습니다 . cat "$p"
파이프에서 변수로 데이터를 읽어온 다음 wait
이를 백그라운드 프로세스에서 사용해야 합니다.
context 없이도 가능 somecommand
하지만 파일 핸들을 사용하려면 더 많은 연습이 필요합니다.
#!/bin/bash
dir=$(mktemp -d)
p="$dir/p"
mkfifo "$p"
# open an fd for read+write (doesn't block because both open)
exec 3<>"$p"
# process substs inherit the fd, closing it when they exit
echo somecommand \
<(false; echo "cmd1: $?" >&3) \
<(true; echo "cmd2: $?" >&3) \
# open another reader to keep the pipe live
exec 4<"$p"
# now we can close the writing handle
exec 3>&-
# read the data off
cat <&4
exec 4<&-
rm -rf "$dir" 2>/dev/null
종료 상태를 일반 파일로 수집하고 알려진 행 수까지 읽는 것이 더 간단할 수 있습니다.
#!/bin/bash
f=$(mktemp)
# number of process substitutions
n=2
echo somecommand \
<(false; echo "cmd1: $?" >>"$f") \
<(true; echo "cmd2: $?" >>"$f") \
exec 3< "$f"
# read that many lines
for ((i = 0; i < n; i++)) do
# if the data isn't there yet, retry reading until a new line appears
until read line <&3; do sleep 1; done
echo "$line";
done
exec 3<&-
rm -f "$f"
테스트한 바에 따르면 세 가지 모두 작동하는 것 같지만 프로세스 대체 및 파이프를 사용하는 것은 번거로울 수 있으므로 일부 실패 모드가 누락될 수 있습니다.