쉘 스크립트가 파이프를 통해 실행되고 있는지 감지하는 방법은 무엇입니까?
표준 출력이 터미널로 전송되거나 다른 프로세스로 파이프되는지 여부를 쉘 스크립트에서 어떻게 감지합니까?
요점은 : 출력을 채색하기 위해 이스케이프 코드를 추가하고 싶지만 대화식으로 실행할 때만 파이프 할 때와 비슷하지만 파이프와는 다릅니다 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
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 입력을 기다립니다.
작동하지 않는 다른 것들
이 문제를 해결하기 위해 다음과 같은 기술을 포함하여 문제를 해결하지 못하는 몇 가지 기술을 살펴 보았습니다.
- SSH 환경 변수 검사
- 사용
stat
은 / dev / stdin을 파일 기술자에 - 통해 대화 형 모드 검사
[[ "${-}" =~ 'i' ]]
- 를 통해 청각 장애의 상태를 검사
tty
하고tty -s
ssh
통해 상태를 검사[[ "$(ps -o comm= -p $PPID)" =~ 'sshd' ]]
/proc
가상 파일 시스템 을 지원하는 OS를 사용하는 경우 STDIO의 기호 링크를 따라 파이프 사용 여부를 판단 할 수 있습니다. 그러나 /proc
크로스 플랫폼, POSIX 호환 솔루션은 아닙니다.
이 문제를 해결하는 데 매우 흥미 롭기 때문에 Linux 및 BSD 모두에서 작동하는 POSIX 기반 솔루션이 작동하는 다른 기술에 대해 생각하면 알려주십시오.
test
(내장 bash
) 명령 에는 파일 디스크립터가 tty인지 확인하는 옵션이 있습니다.
if [ -t 1 ]; then
# stdout is a tty
fi
"참조 man test
"또는 " man bash
"와 "검색 -t
"
어떤 쉘을 사용하고 있는지 언급하지 않지만 Bash에서는 다음과 같이 할 수 있습니다.
#!/bin/bash
if [[ -t 1 ]]; then
# stdout is a terminal
else
# stdout is not a terminal
fi
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
:#
다음 코드 (linux bash 4.4에서만 테스트 됨) 는 이식성이 있거나 권장되지 않아야 하지만 완전성을 위해 여기에 있습니다.
ls /proc/$$/fdinfo/* >/dev/null 2>&1 || grep -q 'flags: 00$' /proc/$$/fdinfo/0 && echo "pipe detected"
왜 그런지 모르겠지만 bash 함수에 STDIN이 파이프 될 때 파일 설명자 "3"이 어떻게 든 생성되는 것 같습니다.
도움이 되길 바랍니다.
'IT story' 카테고리의 다른 글
AngularJS 템플릿의 삼항 연산자 (0) | 2020.04.12 |
---|---|
파일이 비어 있는지 확인하는 방법? (0) | 2020.04.12 |
IntelliJi 아이디어가 시작될 때 열린 마지막 프로젝트를 방지하는 방법 (0) | 2020.04.12 |
jQuery-CSS 클래스가 변경된 경우 Fire 이벤트 (0) | 2020.04.12 |
Kotlin과 새로운 ActivityTestRule : @Rule은 공개되어야합니다 (0) | 2020.04.12 |