[bash] 쉘 스크립트가 파이프를 통해 실행되고 있는지 감지하는 방법은 무엇입니까?

표준 출력이 터미널로 전송되거나 다른 프로세스로 파이프되는지 여부를 쉘 스크립트에서 어떻게 감지합니까?

요점은 : 출력을 채색하기 위해 이스케이프 코드를 추가하고 싶지만 대화식으로 실행할 때만 파이프 할 때가 아닌 것과 비슷합니다 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 명령으로 실행할 때 sshSTD 스트림은 항상 파이프되는 것처럼 보입니다. 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명령이 파이프되고 있는지 여부를 알 수있는 방법이 없음을 의미하기 때문에 상당히 큰 문제 입니다. 이 불행한 동작은 최신 버전의 sshTTY가 아닌 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”이 어떻게 든 생성되는 것 같습니다.

도움이 되길 바랍니다.


답변