프로그램은 stdout이 터미널이나 파이프에 연결되어 있는지 어떻게 알 수 있습니까?

프로그램은 stdout이 터미널이나 파이프에 연결되어 있는지 어떻게 알 수 있습니까?

segfault 이전의 출력이 정확히 필요한 것이기 때문에 segfault 프로그램을 디버깅하는 데 문제가 있습니다. 그러나 출력을 파일로 파이프하면 손실됩니다. 이 답변에 따르면:https://unix.stackexchange.com/a/17339/22615, 이는 프로그램의 출력 버퍼가 터미널에 연결되면 즉시 플러시되지만 파이프에 연결되면 특정 지점에서만 플러시되기 때문입니다. 여기에는 몇 가지 질문이 있습니다.

  • 프로그램은 표준 출력이 무엇에 연결되어 있는지 어떻게 결정합니까?

  • "script" 명령은 프로그램이 터미널에 쓸 때와 동일한 동작을 어떻게 생성합니까?

  • 스크립트 명령 없이 이 작업을 수행할 수 있습니까?

답변1

파일 설명자가 터미널 장치를 가리키는지 확인

프로그램은 다음 명령을 사용하여 파일 설명자가 tty 장치와 연결되어 있는지 여부를 확인할 수 있습니다.isatty()ioctl()표준 C 기능(보통 아래에서는 fd가 tty 장치를 가리키지 않을 때 오류를 반환하는 무해한 tty 관련 시스템 호출을 수행합니다 ).

[/utility는 test연산자를 통해 이를 수행할 수 있습니다 -t.

if [ -t 1 ]; then
  echo stdout is open to a terminal
fi

GNU/Linux 시스템에서 libc 함수 호출 추적:

$ ltrace [ -t 1 ] | cat
[...]
isatty(1)                                      = 0
[...]

시스템 호출 추적:

$ strace [ -t 1 ] | cat
[...]
ioctl(1, TCGETS, 0x7fffd9fb3010)        = -1 ENOTTY (Inappropriate ioctl for device)
[...]

파이프를 가리키는지 확인

fd가 파이프/FIFO와 연관되어 있는지 확인하려면 다음을 사용할 수 있습니다.fstat()시스템 호출st_mode, 이 fd에서 열린 파일의 유형과 권한이 필드에 포함된 구조를 반환합니다 . 이것S_ISFIFO()표준 C 매크로st_mode이 필드에서 fd가 파이프/FIFO인지 확인하는 데 사용할 수 있습니다 .

이를 수행할 수 있는 표준 유틸리티는 없지만 이를 수행할 수 있는 호환되지 않는 명령 구현이 fstat()여러 개 있습니다 . 내장 함수는 패턴을 문자열 표현으로 반환하며 첫 번째 문자는 유형( 파이프의 경우)을 나타냅니다. GNU는 동일한 작업을 수행할 수 있지만 보고 도 해야 합니다.statzshstatstat -sf "$fd" +modepstatstat -c %A - <&"$fd"stat -c %F - <&"$fd"유형홀로. BSD의 경우 stat: stat -f %St <&"$fd"또는 stat -f %HT <&"$fd".

검색 가능한지 확인

응용 프로그램은 일반적으로 stdout이 파이프인지 여부를 신경 쓰지 않습니다. 그들은 검색 가능한지 여부에 관심을 가질 수 있습니다(비록 일반적으로 버퍼링할지 여부를 결정하지는 않지만).

fd가 검색 가능한지 여부를 테스트하려면(파이프, 소켓, tty 장치는 검색할 수 없으며 일반 파일 및 대부분의 블록 장치는 일반적으로 검색 가능) 상대를 시도할 수 있습니다.lseek()시스템 호출오프셋은 0입니다(그래서 무해합니다). dd표준 유틸리티이자 인터페이스이지만 오프셋 0을 요청하면 구현에서 전혀 호출하지 않기 lseek()때문에 이 테스트에서는 작동하지 않습니다 .lseek()

zsh그리고 셸에는 ksh93검색 연산자가 내장되어 있습니다.

$ strace -e lseek ksh -c ': 1>#((CUR))' | cat
lseek(1, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
ksh: 1: not seekable
$ strace -e lseek zsh -c 'zmodload zsh/system; sysseek -w current -u 1 0 || syserror'
lseek(1, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
Illegal seek

버퍼링 비활성화

script명령은 의사 터미널 쌍을 사용하여 프로그램의 출력을 캡처하므로 프로그램의 stdout(stdin 및 stderr도 포함)은 의사 터미널 장치가 됩니다.

stdout이 터미널 장치인 경우 일반적으로 약간의 버퍼링이 있지만 라인 기반입니다. printf/ puts및 co는 개행 문자가 출력될 때까지 아무 것도 쓰지 않습니다. 다른 유형의 파일의 경우 버퍼링은 블록(킬로바이트) 단위로 수행됩니다.

버퍼링을 비활성화하는 몇 가지 옵션이 있으며, 이는 여기의 많은 Q&A에서 논의됩니다(검색버퍼링 해제또는표준 버퍼,컷 출력을 리디렉션할 수 없습니다.일부 방법은 의사 터미널(예: // socat(script)/)을 사용하거나 실행 파일에 코드를 삽입하여 버퍼링을 비활성화하는 것(GNU 또는 FreeBSD에서 수행됨) 중 하나입니다.scriptexpectunbufferexpectzshzptystdbuf

관련 정보