twice
출력을 두 번 보고 싶지만 이 스크립트는 한 번만 출력합니다.
dump() {
(sleep 1; cat) > "$1"
}
(sleep 0; echo "twice") | tee >(dump "./a.txt")
echo "$(< "a.txt")"
두 번 시청하기 위해 수면 일정을 조정해야 했습니다.
dump() {
(sleep 0; cat) > "$1"
}
(sleep 1; echo "twice") | tee >(dump "./a.txt")
echo "$(< "a.txt")"
여기서 경쟁 조건의 원인은 무엇입니까?
답변1
프로세스 교체 호출은 dump
비동기 프로세스로 실행됩니다. 이는 tee
출력이 여기에 기록되고 파이프라인이 완료됨을 의미합니다. tee
작성하면 출력이 버퍼링되므로 파이프가 완료됩니다 .데이터 양이 파이프 버퍼 크기를 초과합니다., 사용하려면 tee
기다려야 하며 원본 코드는 대부분 작동할 것입니다.dump
질문에서 했던 것처럼 소량의 데이터만 쓴다고 가정하면 a.txt
파이프가 종료된 직후 파일에 아무것도 쓸 기회가 생기기 전에 데이터를 읽습니다 dump
(데이터가 보류 중인 상태로 여전히 백그라운드에서 잠자고 있습니다). 파이프 버퍼).
오류 코드를 실행한 후 파일을 보면 a.txt
문자열이 포함되어 있음을 알 수 있습니다 twice
. 따라서 sleep 1
함수에 제공된 약간의 지연 후에 결국에는 해당 위치에 도달합니다.
파이프가 조기에 종료되는 것을 방지하려면 cat
끝에 다음을 추가하십시오.
dump() {
(sleep 1; cat) > "$1"
}
(sleep 0; echo "twice") | tee >(dump "./a.txt") | cat
echo "$(< "a.txt")"
cat
이제 프로세스는 출력이 파이프를 통해 도착할 때까지 기다려야 하기 때문에 작동합니다 dump
(그렇지는 않지만 이를 알지는 못합니다). 이렇게 하면 dump
호출이 반환될 때까지 파이프 종료가 지연됩니다 . 이 시점에서 데이터가 기록되었으며 a.txt
스크립트의 마지막 명령을 사용하여 검색할 수 있습니다.
파이프라인에서 프로세스를 동기화하는 유일한 것은 I/O입니다. 이는 이전 프로세스에서 데이터를 읽고 이를 다음 프로세스에 쓰는 것입니다. 프로세스가 특정 시점에서 이전 단계의 데이터를 읽으려는 경우 읽을 데이터가 있거나 이전 단계에서 파이프 끝을 닫을 때까지 차단됩니다.
cat
기본값은 표준 입력에서 읽는 것입니다. 추가된 표준 입력은 cat
파이프라인의 이전 단계에서 표준 출력 tee
및 프로시저 대체 에 연결됩니다. dump
유틸리티 cat
는 더 이상 읽을 내용이 없을 때까지 읽습니다. 이는 tee
둘 다 실행이 완료될 때까지 dump
발생하지 않습니다 .
코드의 정리된 버전:
dump() {
sleep 1
cat >"$1"
}
echo twice | tee >(dump ./a.txt) | cat
cat a.txt
답변2
(sleep 0; echo "twice") | tee >(dump "./a.txt")
echo "$(< "a.txt")"
IIUC에서 문제는 다음 줄에서 명령 대체를 >(...)
실행하기 전에 내부 프로세스가 완료될 때까지 기다리는 방법입니다.$(...)
대답은 이를 수행하는 좋은 방법이 없다는 것입니다. 시스템이 이 /dev/fd/
메커니즘을 지원하는 경우 다음과 같은 트릭을 사용할 수 있습니다 exec fd> >(...)
.
dump() {
(sleep 1; cat) > "$1"
}
echo twice | { exec 7> >(dump a.txt); tee /dev/fd/7; exec 7>&-; wait; }
echo "$(< "a.txt")"
예, 보기 흉하지만 더 나쁠 수도 있습니다.
echo twice | { a=>(dump a.txt); tee "$a"; eval "exec ${a##*/}>&-"; wait; }
이러한 목적으로 수집될 수 있기 때문에ㅏ)최신 버전의 bash(>= 5.0)를 사용하면 wait
내부에서 프로세스를 실행할 수 있습니다.>(...)
비)/dev/fd/63
이러한 프로세스는 표준 입력에서 EOF를 얻을 때까지 종료되지 않을 수 있으며, 이는 파이프( 또는 유사한 >(...)
확장 파이프) 의 다른 쪽 끝을 닫을 때까지 발생하지 않습니다 . 후자는 올바르게 수행하기가 어렵습니다.
답변3
일부 Bash 버전에는 생성된 프로세스를 대체할 프로세스를 올바르게 기다리지 못하게 하는 버그가 있습니다.