경쟁 조건이있는 이유
파이프의 양면은 차례로 병렬로 실행됩니다. 이것을 보여주는 매우 간단한 방법이 있습니다 : run
time sleep 1 | sleep 1
2 초가 아닌 1 초가 걸립니다.
쉘은 두 개의 하위 프로세스를 시작하고 두 프로세스가 완료되기를 기다립니다. 이 두 과정은 병렬로 실행 :이 때 그들 중 하나가 다른와 동기화 할 유일한 이유는 필요가 다른 기다릴. 가장 일반적인 동기화 지점은 오른쪽이 표준 입력에서 데이터 읽기를 기다리는 것을 차단하고 왼쪽이 더 많은 데이터를 쓸 때 차단 해제됩니다. 오른쪽이 더 많은 데이터를 읽을 때까지 (오른쪽에서 더 많은 데이터를 읽을 때까지 오른쪽에서 데이터 읽기 속도가 느리고 왼쪽 블록이 쓰기 작업을 수행하는 경우에도 반대가 발생할 수 있습니다. 커널이지만 최대 크기는 작습니다).
동기화 지점을 관찰하려면 다음 명령을 관찰하십시오 ( sh -x
각 명령이 실행될 때 인쇄).
time sh -x -c '{ sleep 1; echo a; } | { cat; }'
time sh -x -c '{ echo a; sleep 1; } | { cat; }'
time sh -x -c '{ echo a; sleep 1; } | { sleep 1; cat; }'
time sh -x -c '{ sleep 2; echo a; } | { cat; sleep 1; }'
관찰 한 내용이 편할 때까지 변형을 가지고 연주하십시오.
복합 명령이 주어지면
cat tmp | head -1 > tmp
왼쪽 프로세스는 다음을 수행합니다 (내 설명과 관련된 단계 만 나열했습니다).
cat
인수를 사용 하여 외부 프로그램 을 실행하십시오 tmp
.
tmp
읽기 위해 엽니 다 .
- 파일 끝에 도달하지 않은 상태에서 파일에서 청크를 읽고 표준 출력에 씁니다.
오른쪽 프로세스는 다음을 수행합니다.
- 표준 출력을로 리디렉션
tmp
하여 프로세스에서 파일을 자릅니다.
head
인수를 사용 하여 외부 프로그램 을 실행하십시오 -1
.
- 표준 입력에서 한 줄을 읽고 표준 출력에 씁니다.
유일한 동기화 지점은 right-3이 left-3이 하나의 전체 회선을 처리 할 때까지 대기한다는 것입니다. left-2와 right-1 사이에는 동기화가 없으므로 어느 순서로든 발생할 수 있습니다. 이들이 발생하는 순서는 예측할 수 없습니다 .CPU 아키텍처, 셸, 커널, 프로세스가 예약되는 코어, 해당 시간 동안 CPU가 수신하는 인터럽트 등에 따라 다릅니다.
행동을 바꾸는 방법
시스템 설정을 변경하여 동작을 변경할 수 없습니다. 컴퓨터가 지시 한대로 수행합니다. 병렬 로 자르고 tmp
읽도록 지시 tmp
했기 때문에 두 가지를 동시에 수행합니다.
좋아, 변경할 수있는 "시스템 설정"이 하나 있습니다 : /bin/bash
bash가 아닌 다른 프로그램으로 대체 할 수 있습니다 . 나는 이것이 좋은 생각이 아니라고 말할 수 있기를 바랍니다.
파이프 왼쪽보다 먼저 잘림을 원하면 파이프 라인 외부에 잘라 내야합니다. 예를 들면 다음과 같습니다.
{ cat tmp | head -1; } >tmp
또는
( exec >tmp; cat tmp | head -1 )
나는 왜 당신이 이것을 원할 지 모른다. 비어있는 파일을 읽을 때 요점은 무엇입니까?
반대로 cat
읽기를 마친 후에 출력 리디렉션 (잘림 포함)이 발생 하도록하려면 메모리에 데이터를 완전히 버퍼링해야합니다.
line=$(cat tmp | head -1)
printf %s "$line" >tmp
또는 다른 파일에 쓴 다음 제자리로 이동하십시오. 이것은 일반적으로 스크립트에서 작업을 수행하는 강력한 방법이며 파일이 원래 이름으로 표시되기 전에 전체가 작성된다는 이점이 있습니다.
cat tmp | head -1 >new && mv new tmp
moreutils의 컬렉션 단지라는 것을 수행하는 프로그램을 포함한다 sponge
.
cat tmp | head -1 | sponge tmp
자동으로 문제를 감지하는 방법
목표가 잘못 작성된 스크립트를 가져 와서 스크립트가 깨지는 위치를 자동으로 파악하는 것이 목적이라면 인생은 그렇게 간단하지 않습니다. cat
잘림이 발생하기 전에 때때로 읽기를 완료하기 때문에 런타임 분석에서 문제를 확실하게 찾을 수 없습니다 . 정적 분석은 원칙적으로 가능합니다. 귀하의 질문에 단순화 된 예는 Shellcheck 에 의해 잡히지 만 더 복잡한 스크립트에서 비슷한 문제를 잡을 수는 없습니다.