스크립트 내부의 표준 출력이 리디렉션되었는지 어떻게 알 수 있나요?

스크립트 내부의 표준 출력이 리디렉션되었는지 어떻게 알 수 있나요?

ksh에 스크립트가 있습니다. 명령줄에서 리디렉션이 있는지 여부에 따라 를 전달합니다 exec 1>file. 스크립트를 호출하는 명령이 출력을 리디렉션한 경우 스크립트 자체 내에서 이를 어떻게 테스트할 수 있습니까?

스크립트의 PID에서 $@, $*, $0, 및 a를 사용해 보았지만 ps(셔뱅과 함께) 리디렉션이 나타나지 않습니다.

이 예에서 스크립트는 AIX에서 실행됩니다.

답변1

AIX에서는 stdout 파일 설명자를 에서 사용할 수 /proc/$$/fd/1있으므로 일반 파일인지 테스트할 수 있습니다.

if [ -f "/proc/$$/fd/1" ]
then
  echo stdout has already been redirected
else
  echo redirecting stdout
  exec 1>file
  echo some output
fi

/bin/sh는 /bin/ksh에 하드 링크되어 있으므로 두 쉘 모두에서 동일한 동작을 얻습니다.

필요한 경우 stdout이 /dev/null로 리디렉션되었는지 별도로 테스트할 수 있습니다.

if [ "/proc/$$/fd/1" -ef /dev/null ]; then : ...; fi

답변2

일반적으로 말하면 할 수 없습니다. 리디렉션은 실행 명령에 대한 인수로 표시되지 않습니다. 그렇게 하더라도 모든 경우에 스크립트 출력이 어디로 가는지 알 수 없습니다. 다음 두 가지를 고려하십시오.

bash -c 'somecmd > /dev/null; othercmd'

그리고

bash -c 'somecmd; othercmd'  > /dev/null

첫 번째 경우에는 의 출력이 somecmd로 리디렉션되지만 , 두 번째 경우에는 및 을 /dev/null포함한 전체 셸의 출력이 리디렉션됩니다 . 두 번째 경우의 명령줄을 보면 출력이 어떻게 리디렉션되는지 알 수 없습니다.somecmdothercmdsomecmd

즉, Bash의 DEBUG트랩은 이러한 목적에 유용한 것으로 보입니다.

$ trap 'export CMDLINE=$BASH_COMMAND' DEBUG
$ env 2>/dev/null |grep CMD
CMDLINE=env 2> /dev/null

트랩은 실행할 명령을 내보냅니다 CMDLINE. 의 출력에 해당 명령이 표시되므로 해당 명령이 내보내지는 것을 볼 수 있습니다 env. 전체 파이프라인은 표시되지 않고 단일 명령만 표시됩니다.


즉, 대부분의 경우 사용자의 리디렉션을 추측하는 것보다 문제를 처리하는 더 나은 방법이 있습니다. 많은 명령은 출력이 터미널로 전송되는지 확인하고 그에 따라 동작을 변경합니다.

stdout이 터미널인지 확인하려면 다음을 사용할 수 있습니다 [ -t 1 ].

$ if [ -t 1 ]; then echo terminal; else echo not terminal; fi  |cat
not terminal

이는 출력이 터미널에 도달하지 않아 가정에 따라 사용자에게 도달하지 않는 경우 특정 대화형 기능이나 외부 출력을 비활성화하는 데 가장 자주 사용됩니다.


파일 설명자가 터미널을 가리키는지 테스트하는 것만으로는 충분하지 않은 경우 가장 간단한 접근 방식은 프로그램에 추가 인수를 전달하여 실행할 모드를 알려주는 것입니다. 즉, 리디렉션에 신경 쓰지 않고 프로그램이 로 시작하면 한 가지 작업을 수행하고 someprog --mode=cron로 시작하면 다른 작업을 someprog --mode=batch수행하며 인수 없이 시작되면 대화형으로 실행되도록 합니다 --mode. (대화형 또는 명령줄 모드를 기본값으로 설정하면 사용자가 --mode=commandline수동으로 실행할 때마다 수동으로 입력할 필요가 없습니다.)

답변3

내 테스트에서는 5.0.3(1)-release (x86_64-pc-linux-gnu)Linux(Debian 10.12)에서 Bash 5(GNU bash, 버전)를 사용하여 테스트가 작동했습니다 [ -p.

  • 스크립트를 고려해보세요test.sh
    #!/bin/bash
    ls -lah /proc/$$/fd/1
    if [ -p "/proc/$$/fd/1" ]; then echo "pipe"; else echo "nopipe"; fi
    exec 1> >( cat ) 2>&1
    ls -lah /proc/$$/fd/1
    if [ -p "/proc/$$/fd/1" ]; then echo "pipe"; else echo "nopipe"; fi
    
  • 리디렉션 없이 또는 리디렉션을 사용하여 스크립트를 실행합니다.
    $ ./test.sh
    lrwx------ 1 [...] /proc/123/fd/1 -> /dev/pts/2
    nopipe
    l-wx------ 1 [...] /proc/123/fd/1 -> pipe:[12345]
    pipe
    
    $ ./test.sh | cat
    l-wx------ 1 [...] /proc/124/fd/1 -> pipe:[12346]
    pipe
    l-wx------ 1 [...] /proc/124/fd/1 -> pipe:[12347]
    pipe
    

관련 정보