{
echo bla1
echo bla2 1>&2
} >>myfile 2>&1
두 -s 사이에 차이점이 있습니까 echo
?
내가 생각할 수 있는 유일한 차이점은 echo bla2 2>&1
stderr의 버퍼링되지 않은 속성을 보존한다는 것입니다.
어때요?
{
echo bla1
echo bla2 1>&2 &
} >>myfile 2>&1
?
같은가요?
{
echo bla1
echo bla2 &
} >>myfile 2>&1
?
그러면 fd1과 fd2는 {...} >>myfile 2>&1
본질적으로 동일하게 될까요, 아니면 과거의 상태로 유지됩니까?
편집하다:
@ilkkachu의 답변을 바탕으로 다음을 시도했습니다.
perl -e 'print "foo"; sleep 2; print "bar\n"; sleep 2; print "bla"'
# ^^^line buffered
perl -e 'print "foo"; sleep 2; print "bar\n"; sleep 2; print "bla"' | cat
# ^^^block buffered
touch myfile; tail -f myfile &
perl -e 'print "foo"; sleep 2; print "bar\n"; sleep 2; print "bla"' >>myfile 2>&1
# ^^^block buffered
perl -e 'print STDERR "foo"; sleep 2; print STDERR "bar\n"; sleep 2; print STDERR "bla"'
# ^^^unbuffered
perl -e 'print STDERR "foo"; sleep 2; print STDERR "bar\n"; sleep 2; print STDERR "bla"' 2>&1 | cat
# ^^^unbuffered
perl -e 'print STDERR "foo"; sleep 2; print STDERR "bar\n"; sleep 2; print STDERR "bla"' >>myfile 2>&1
# ^^^unbuffered
fd1의 경우 버퍼링은 출력 유형에 따라 조정됩니다.
fd2의 경우 출력은 중요하지 않습니다.
{ echo -n foo; sleep 2; echo bar; sleep 2; echo -n bla; }
# ^^^unbuffered
{ echo -n foo; sleep 2; echo bar; sleep 2; echo -n bla; } | cat
# ^^^unbuffered
touch myfile; tail -f myfile &
{ echo -n foo; sleep 2; echo bar; sleep 2; echo -n bla; } >>myfile 2>&1
# ^^^unbuffered
{ echo -n foo 1>&2; sleep 2; echo bar 1>&2; sleep 2; echo -n bla 1>&2; }
# ^^^unbuffered
{ echo -n foo 1>&2; sleep 2; echo bar 1>&2; sleep 2; echo -n bla 1>&2; } | cat
# ^^^unbuffered
{ echo -n foo 1>&2; sleep 2; echo bar 1>&2; sleep 2; echo -n bla 1>&2; } >>myfile 2>&1
# ^^^unbuffered
echo
출력이 어디로 가든지 항상 플러시되는 것 같습니다 .
실험을 반복하세요
{ printf foo; sleep 2; printf 'bar\n'; sleep 2; printf bla; }
이번에도 결과는 동일합니다 echo
.
결과: 위의 경우 버퍼링은 작성자가 결정합니다.
답변1
출력 버퍼링의 일반적인 발생은 리디렉션이나 셸과 관계없이 C 런타임 라이브러리의 속성입니다.
프로세스가 시작되면 일부 파일 설명자만 가져옵니다. 그런 다음 런타임 라이브러리는 내부 구조 및 관련 버퍼(있는 경우)를 빌드합니다. 관심 있는 것은 fd 1(stdout의 경우)이 터미널에 연결되어 있는지 여부입니다. 그렇다면 라인 버퍼링을 수행하고, 그렇지 않은 경우 블록 버퍼링을 수행합니다. 어쨌든 Stderr는 버퍼링되지 않습니다. 주로 성능과 관련하여 출력을 쓰기 위한 시스템 호출은 상대적으로 비용이 많이 들고 출력이 파일이나 파이프로 이동하는 경우 대기 시간보다 대역폭이 더 중요하다고 가정됩니다.
물론 버퍼링 동작은 유틸리티에 따라 달라질 수 있으며 일부 도구는 C 라이브러리 동작 자체를 다시 구현할 수도 있습니다. (Perl이나 Python과 같은 것이 이 작업을 수행할 수 있다고 생각하지만, 내가 아는 한 그들은 어쨌든 규칙을 따릅니다.) 가정이 성립하지 않는 경우 일부 유틸리티에는 버퍼링을 제어하는 옵션도 있습니다(예: 적어도 --line-buffered
GNU 및 FreeBSD grep), 스스로 할 수 없는 도구에 강제로/속일 수 있는 도구가 있습니다. 다음을 참조하세요.파이프라인에서 버퍼링 끄기
그럼에도 불구하고 출력은 echo
버퍼링되지 않을 수 있습니다. 외부 명령인 경우 반환하기 전에 버퍼를 플러시해야 한다는 간단한 이유 때문입니다. 쉘 처리 자체가 echo
이를 버퍼링할 수 있지만 (*) .
Perl을 테스트 도구로 사용하려면 지연(블록 버퍼링) 직후 모든 출력이 인쇄됩니다.
$ perl -e '"foo\n" 인쇄; sed -e 's/^/:/' [지연...] :부자 :술집
그러면 다음 줄이 즉시 인쇄되며 그 사이에 잠자기 상태가 됩니다.
$ perl -e 'STDERR "foo\n" 인쇄 2>&1 sed -e 's/^/:/' :부자 [지연...] :술집
(여기서 중요한 점 sed
은 출력이 파이프를 통해 터미널로 가는 것이 아니라 파이프를 통과하는지 확인하는 것입니다.)
간단한 C 프로그램으로도 동일한 작업을 수행할 수 있습니다.
귀하의 질문에서 리디렉션 이 stdout, fd 1로 인쇄되므로 리디렉션된 파일 설명자를 건드리지 않기 때문에 리디렉션이 무엇을 echo bla2 2>&1
해야 하는지 잘 모르겠습니다 . echo
반대 방향으로 이 작업을 수행하면 somecommand 1>&2
명령은 관련 버퍼링과 함께 자체 표준 출력에 계속 기록됩니다.
다음과 같습니다(이번에는 라인 버퍼링 사용).
$ perl -e '"foo" 인쇄 2; "bar\n" 인쇄;' [지연...] 푸바
물론 echo
이것은 종료 시 버퍼를 플러시하는 유일한 도구는 아닙니다. 버퍼 플러시를 트리거하기 전에 종료하면 다른 도구와 동일한 결과를 얻게 됩니다. 예를 들어, 두 perl
부분 모두 라인 버퍼링을 사용 하더라도 두 부분으로 인쇄됩니다 .
$ perl -e '"foo" 인쇄'; perl -e '"bar\n" 인쇄;' 부자[지연...]술집
(* 및 ksh는 경우에 따라 버퍼링되는 것 같습니다. 기본적으로 사이에 다른 작업이 많이 있지 않는 한 echo foo; echo bar;
한 번의 write()
호출과 같은 작업을 최적화합니다. 내가 가지고 있는 버전을 제외하고 지연된 루프는 해당 동작을 보여줍니다. 그런 경우 ksh -c 'echo foo; i=0; while [ "$i" -lt 234567 ]; do i=$((i + 1)); done; echo bar'
먼저 지연이 발생합니다. , 그 다음 foo
및 bar
, 그러나 이는 약간 특별한 경우입니다).