표준 출력이 터미널로 전송되거나 다른 프로세스로 파이프되는지 여부를 쉘 스크립트에서 어떻게 감지합니까?
요점은 : 출력을 채색하기 위해 이스케이프 코드를 추가하고 싶지만 대화식으로 실행할 때만 파이프 할 때가 아닌 것과 비슷합니다 ls --color
.
표준 출력이 터미널로 전송되거나 다른 프로세스로 파이프되는지 여부를 쉘 스크립트에서 어떻게 감지합니까?
요점은 : 출력을 채색하기 위해 이스케이프 코드를 추가하고 싶지만 대화식으로 실행할 때만 파이프 할 때가 아닌 것과 비슷합니다 ls --color
.
답변:
순수한 POSIX 셸에서
if [ -t 1 ] ; then echo terminal; else echo "not a terminal"; fi
출력이 터미널로 전송되기 때문에 "터미널"을 반환합니다.
(if [ -t 1 ] ; then echo terminal; else echo "not a terminal"; fi) | cat
괄호의 출력이로 파이프되기 때문에 "터미널이 아님"을 반환합니다 cat
.
이 -t
플래그는 매뉴얼 페이지에서
-t fd 파일 디스크립터 fd가 열려 있고 터미널을 참조하면 참입니다.
... 여기서 fd
일반적인 파일 디스크립터 할당 중 하나 일 수 있습니다.
0: stdin
1: stdout
2: stderr
-t
플래그는 POSIX에 지정되므로 POSIX 호환 쉘 (즉, bash 확장이 아님)에 작동해야합니다. pubs.opengroup.org/onlinepubs/009695399/utilities/test.html
fish
쉘 답변을 찾고있었습니다 . 사용 test
은 깔끔하지만 괄호로 묶은 예제는 지원되지 않으므로 시도 할 수 없습니다. 비슷한 것으로 래핑을 시도 begin; ...; end
했지만 작동하지 않는 것처럼 보였고 양수 코드 블록을 다시 실행했습니다. 사용해야 status
한다고 생각 했지만 배관을 확인하지 않는 것 같습니다. 이러한 명확한 답변 덕분에 이전 명령 / 스크립트의 STDOUT이 터미널로 설정되어 있지 않은지 본질적으로 확인하고 싶습니다.
STDIN, STDOUT 또는 STDERR이 주로와 같은 프로그램으로 인해 스크립트에서 파이프로 또는 파이프에서 파이프되는지 여부를 결정하는 확실한 방법 은 없습니다 ssh
.
예를 들어, 다음 bash 솔루션은 대화식 쉘에서 올바르게 작동합니다.
[[ -t 1 ]] && \
echo 'STDOUT is attached to TTY'
[[ -p /dev/stdout ]] && \
echo 'STDOUT is attached to a pipe'
[[ ! -t 1 && ! -p /dev/stdout ]] && \
echo 'STDOUT is attached to a redirection'
그러나이 명령을 비 TTY 명령으로 실행할 때 ssh
STD 스트림은 항상 파이프되는 것처럼 보입니다. STDIN을 사용하면 쉽게 설명 할 수 있습니다.
# CORRECT: Forced-tty mode correctly reports '1', which represents
# no pipe.
ssh -t localhost '[[ -p /dev/stdin ]]; echo ${?}'
# CORRECT: Issuing a piped command in forced-tty mode correctly
# reports '0', which represents a pipe.
ssh -t localhost 'echo hi | [[ -p /dev/stdin ]]; echo ${?}'
# INCORRECT: Non-tty mode reports '0', which represents a pipe,
# even though one isn't specified here.
ssh -T localhost '[[ -p /dev/stdin ]]; echo ${?}'
이것은 bash 스크립트가 비 tty ssh
명령이 파이프되고 있는지 여부를 알 수있는 방법이 없음을 의미하기 때문에 상당히 큰 문제 입니다. 이 불행한 동작은 최신 버전의 ssh
TTY가 아닌 STDIO에 파이프를 사용하기 시작할 때 도입되었습니다 . 이전 버전은 소켓을 사용했으며, 소켓을 사용하여 bash 내에서 구별 할 수 있습니다.[[ -S ]]
.
이 제한은 일반적으로와 같은 컴파일 된 유틸리티와 유사한 동작을하는 bash 스크립트를 작성하려고 할 때 문제를 일으 킵니다 cat
. 예를 들어, cat
다양한 입력 소스를 동시에 처리 할 때 다음과 같은 유연한 동작을 수행 할 수 있으며 비 TTY 또는 강제 TTY 사용 여부에 관계없이 파이프 입력을 수신하는지 여부를 판단 할 수 있습니다 ssh
.
ssh -t localhost 'echo piped | cat - <( echo substituted )'
ssh -T localhost 'echo piped | cat - <( echo substituted )'
파이프가 관련되어 있는지 여부를 확실하게 확인할 수있는 경우에만 이와 같은 작업을 수행 할 수 있습니다. 그렇지 않으면 파이프 또는 경로 재 지정에서 입력을 사용할 수 없을 때 STDIN을 읽는 명령을 실행하면 스크립트가 정지되고 STDIN 입력을 기다립니다.
이 문제를 해결하기 위해 다음과 같은 기술을 포함하여 문제를 해결하지 못하는 몇 가지 기술을 살펴 보았습니다.
stat
은 / dev / stdin을 파일 기술자에[[ "${-}" =~ 'i' ]]
tty
하고tty -s
ssh
통해 상태를 검사[[ "$(ps -o comm= -p $PPID)" =~ 'sshd' ]]
/proc
가상 파일 시스템 을 지원하는 OS를 사용하는 경우 STDIO의 기호 링크를 따라 파이프 사용 여부를 판단 할 수 있습니다. 그러나 /proc
크로스 플랫폼, POSIX 호환 솔루션은 아닙니다.
이 문제를 해결하는 데 매우 흥미 롭기 때문에 Linux 및 BSD 모두에서 작동하는 POSIX 기반 솔루션이 작동하는 다른 기술에 대해 생각하면 알려주십시오.
stat
/ dev / stdin 의 호출 결과에는 차이가 없습니다 . 그리고 왜 않습니다 "${-}"
또는 tty -s
작동하지? 또한 소스 코드를 살펴 보았지만 cat
POSIX 셸에서 할 수없는 마법을 수행하는 부분을 보지 못했습니다. 그것에 대해 자세하게 말 해 주실 수 있나요?
test
(내장 bash
) 명령 에는 파일 설명자가 tty인지 확인하는 옵션이 있습니다.
if [ -t 1 ]; then
# stdout is a tty
fi
"참조 man test
"또는 " man bash
"와 "검색 -t
"
help test
(및 help help
기타) info bash
을 참조하십시오. 이 명령은 오프라인에서 스크립팅을 끝내거나 더 광범위하게 이해하려는 경우에 유용합니다.
Solaris에서는 Dejay Clayton의 제안이 대부분 작동합니다. -p가 원하는대로 응답하지 않습니다.
bash_redir_test.sh는 다음과 같습니다.
[[ -t 1 ]] && \
echo 'STDOUT is attached to TTY'
[[ -p /dev/stdout ]] && \
echo 'STDOUT is attached to a pipe'
[[ ! -t 1 && ! -p /dev/stdout ]] && \
echo 'STDOUT is attached to a redirection'
Linux에서는 다음과 같이 작동합니다.
:$ ./bash_redir_test.sh
STDOUT is attached to TTY
:$ ./bash_redir_test.sh | xargs echo
STDOUT is attached to a pipe
:$ rm bash_redir_test.log
:$ ./bash_redir_test.sh >> bash_redir_test.log
:$ tail bash_redir_test.log
STDOUT is attached to a redirection
Solaris에서 :
:# ./bash_redir_test.sh
STDOUT is attached to TTY
:# ./bash_redir_test.sh | xargs echo
STDOUT is attached to a redirection
:# rm bash_redir_test.log
bash_redir_test.log: No such file or directory
:# ./bash_redir_test.sh >> bash_redir_test.log
:# tail bash_redir_test.log
STDOUT is attached to a redirection
:#