두 개의 비동기 서브셸 명령을 공유 표준 출력에 안전하게 쓸 수 있습니까?

두 개의 비동기 서브셸 명령을 공유 표준 출력에 안전하게 쓸 수 있습니까?

비동기적으로 실행되는 두 개의 bourne(또는 중요한 경우 bash) 하위 쉘 명령으로 stdout을 덮어쓸 수 있습니까?

(tail -f ./file1 & tail -f ./file2) | cat

행 순서는 신경 쓰지 않고 각 출력 행이 하나의 입력 행으로 구성된다는 점만 다릅니다. 일부 행이 부분적으로 가려지거나 인터리브될 수 있다는 점이 걱정됩니다.

저는 각각 고유한 라인을 1,500만 번 출력하는 4개의 명령을 실행하여 이를 테스트했습니다. 작동하는 것 같지만 실패할 것으로 예상했습니다.

누군가 이것이 어떻게 깨지지 않는지 설명할 수 있습니까? 각 하위 쉘은 버퍼링되어 있으며 한 번에 하나의 하위 쉘만 표준 출력에 쓸 수 있습니까? 또는 이것이 어떻게 관리되는지.

더 좋은 방법이 있나요?

(내가 그렇다는 걸 신경쓰지 마세요.사용tail위의 서브쉘에서예시 목적. 실제로 한 번에 한 줄씩 표준 출력으로 지속적으로 출력하는 두 개의 다른 명령을 실행하고 싶습니다. )

답변1

껍질은 거기에 거의 관여하지 않았습니다. 그들이 하는 일은 파이프라인을 생성하고 이 3개의 명령을 실행하는 것뿐입니다. 그런 다음 이 명령은 셸과 독립적으로 병렬로 실행됩니다.

여기서 중요한 것은 두 tail 명령 모두 동일한 파이프의 동일한 쓰기 끝에 파일 설명자를 쓴다는 것입니다.

이렇게 하면:

printf foo1 >> file1; sleep 1
printf foo2 >> file2; sleep 1
printf 'bar1\n' >> file1; sleep 1
printf 'bar2\n' >> file2

당신이 볼 수 있는 것:

foo1foo2bar1
bar2

왜냐면 그렇게 쓰여있으니까요. 명령이 다음을 출력하는지 확인해야 합니다.가득한한 번에 하나의 행을 쓰며 행은 PIPE_BUF(Linux의 경우 4096바이트)보다 작아서 write()가 원자적임을 보장합니다(모두 가득 차 있고 누적 크기가 더 작은 경우 여러 개의 전체 행을 동시에 쓸 수도 있습니다). PIPE_BUF보다).

GNU를 사용하면 grep다음 명령을 전달하여 이를 수행할 수 있습니다.grep --line-buffered '^'

(tail -f ./file1 | grep --line-buffered '^' &
 tail -f ./file2 | grep --line-buffered '^') | cat

이렇게 하면 두 명령 출력의 각 줄에 write()시스템 호출이 포함됩니다(명령이 출력의 마지막 줄을 종료하지 않는 경우 grep누락된 줄 바꿈이 추가됩니다).

답변2

1. 나쁜 해결책

기본 구성에서 stderr은아니요버퍼링되어 있지만 표준 출력은 버퍼링되어 있습니다.

따라서 문제에 대한 가장 간단한 해결책은

  1. 모든 것을 한 줄씩 작성하는 데 사용되는 도구를 제공합니다.
  2. 출력을 stderr( >&2) 로 리디렉션합니다.

하지만 이 버퍼링은 프로세스 내부의 C 라이브러리에서 발생하기 때문에 작동하지 않습니다. 표준 출력이 표준 오류로 리디렉션되면 해당 표준 오류도 버퍼링되지 않습니다.

2. 더 나은 솔루션

출력을 모든 것을 읽는 프로세스로 파이프한 다음 한 줄씩 기록합니다. 가장 간단한 방법은

tool1 | while read; do echo "$REPLY"; done & tool2 | while read; do echo "$REPLY"; done

여러 명령/스크립트의 "아름다운" 병렬 실행을 위해,여기내 다른 답변을 읽을 수 있습니다.

3. 실제 솔루션

write(1, ...)불행하게도 프로세스 출력은 대부분 libc에 의해 버퍼링됩니다. libc의 내부 내용은 출력을 커널 수준 호출 에 매핑하는 방법입니다 . 그것은 그들에게 달려 있습니다. 이를 변경하지 않으면 아직 작성되지 않은 출력을 제어할 수 없습니다.

FILE*libc의 메커니즘을 쓰기에 사용한다면,버퍼 설정(3)그리고플러시(3)함수는 당신의 친구가 될 수 있습니다.

관련 정보