출력 리디렉션을 동적으로 사용하는 방법은 무엇입니까?

출력 리디렉션을 동적으로 사용하는 방법은 무엇입니까?

스크립트에 디버깅 옵션을 추가하려고 합니다. 보통 경고 등의 출력을 숨기고 싶어서 >/dev/null 2>&1명령을 많이 넣습니다.

이제 스크립트를 디버그하려면 이러한 스크립트를 수동으로 제거하거나 각 명령을 if ... fitest 변수 에 넣어야 합니다 $DEBUG.

>/dev/null 2>&1변수( $REDIR)를 넣고 글을 쓰는 것이 command arg1 $REDIR효과가 있을 것 같습니다 . 디버깅하고 싶다면 그냥 비워두세요 $REDIR.

하지만 내 쉘에서 간단한 테스트를 해본 결과, 그런 식으로 작동하지 않는 것으로 나타났습니다.

~$ echo "bla" >/dev/null 2>&1
~$ REDIR=>/dev/null 2>&1
~$ echo "bla" $REDIR
bla
~$

명백한 이유로 "or '주변을 사용하는 것은 >/dev/null 2>&1작동하지 않습니다.

그렇다면 왜 내 아이디어가 여기서 작동하지 않습니까? 명령어 등을 변수에 넣고 호출하는 것을 제가 잘못 이해한 걸까요?

답변1

리디렉션은 명령이 아니므로 이 방법으로 실행할 수 없습니다. 을 사용하면 완료할 수 있지만 eval문제가 발생합니다.

원하는 작업을 수행하는 더 좋은 방법은 디버그 출력을 위한 함수를 갖는 것입니다.

function debugprint {
    if [ ! -z "$debug" ]; then
        echo "$1"
    fi
}

debugprint "$(echo 'bla' 2>&1)"

이는 표준 오류를 표준 출력으로 리디렉션한 다음 debugprint출력을 인수로 사용하여 호출됩니다. 이제 디버깅을 원할 때 해야 할 일은 $debugnull이 아닌 것으로 설정하는 것뿐입니다.

물론, 이것은 (인용과 관련된) 웜의 (다른) 캔을 열 수도 있습니다. 그냥 사용하고 싶을 수도 있는데 set -x, 이는 디버깅 요구 사항에 충분할 수도 있고 충분하지 않을 수도 있습니다.

답변2

이를 위해 나는 보통 다음과 같은 함수를 정의합니다 run. 대부분의 경우 이는 공백이 있는 매개변수와 기타 매개변수를 올바르게 처리합니다.

#!/bin/bash

run() {
        if $DEBUG; then
                v=$(exec 2>&1 && set -x && set -- "$@")
                echo "#${v#*--}"
                "$@"
        else
                "$@" >/dev/null 2>&1
        fi
}

DEBUG=false
run echo "bla"

DEBUG=true
run echo "bla"
run printf "%s . %s . %s\n" bla "more bla" bla

산출:

$ bash debug.sh 
# echo bla
bla
# printf '%s . %s . %s\n' bla 'more bla' bla
bla . more bla . bla

답변3

귀하의 경우 echo는 $REDIR문자열 매개변수로 처리됩니다. 당신은 다음과 같은 것을 원합니다 :

~$ echo "bla" >/dev/null 2>&1
~$ REDIR='>/dev/null 2>&1'
~$ eval "echo bla $REDIR"
~$

그러나 빠르고 더러운 해킹을 시도하지 않는 한 Wouter Verhelst가 더 나은 솔루션을 제공합니다(실제로 그렇게 길거나 복잡하지 않습니다).

답변4

나는 내가 작성하는 모든 쉘 함수에 대해 동일한 작업을 수행합니다.

fn(){ 
    echo some normal stderr debug stuff             >&2     #if $DBG 2>stderr
    dd if="\$DBG/please/report/on/this/file"                #ditto
    echo I DEFINITELY need to handle this           >&3     #always stderr
    ( PATH=; ".some" oops I expect to handle )     2>&4     #always /dev/null
    echo and the regular stuff                              #always unaffected
}   4<>/dev/null 3>&2 2>&"$((${#DBG}?3:4))"

나는 몇 가지 이유로 이것을 좋아합니다.

  1. len을 사용한 ${#DBG}평가는 항상 보장됩니다.>=0테스트의 정수 값 - $DBG실제로 포함된 모든 값입니다. DBG=IFS=0예를 들어, 이는 수학을 안전하게 만듭니다.

  2. 함수가 실행될 때마다 실제 작업은 한 번만 수행됩니다 open(). 그 /dev/null외에는 ./dev/null#fd>&4

  3. 디버그 출력(활성화할 수 있음 set -x)은 기본적으로 대화형 셸에 함수를 덤프합니다.~하지 않는 한환경 변수를 $DBGnull이 아닌 값으로 명시적으로 설정했습니다.

    • 비어 있지 않은 경우 $DBG리디렉션된 수학적 확장은 자체를 가리키며 이는 로 평가됩니다 2>&3.
    • 그러나 그 외에는 공개 설명자로 평가되므로 2>&4공개 /dev/null설명자로 이동합니다.
    • 일반적으로 식별하고 저장한 함수의 20줄 실행 추적을 볼 필요는 없지만 ~/.sh/fn/..., 식별했다면 관련된 명령줄은 완전히 다른 것일 수 있습니다 set -x.
    • $DBGnull/not set인 경우에도 편리하지만set -x $PS4stderr로 이동하지 않지만 #fd>&3여전히 도달할 수 있는 명령별 평가 종료를 열기 때문에 활성화됩니다 .
  4. 합리적인 방식으로 상속을 구현합니다.

    • 다른 함수를 호출하는 함수는 $DBG어떤 방식으로든 이 값에 영향을 미칠 수 없습니다. 따라서 이미 쓸 수 없는 경우 기본적으로 stderr에 쓰기를 시작할 수 있습니다.
    • 설정되지 않거나 null 인 경우 $DBG호출하는 모든 하위 함수는 #fd3호출하지 않는 한 Even의 의미를 잃습니다 child_fn 2>&3. 이 경우 상위 함수가 명시적으로 stderr에 쓰는 것과 동일한 기회를 얻습니다.
    • 그것할 수 있는설정하더라도 $DBG이러한 하위 기능을 조용하게 설정하지 않습니다.$DBG
    • 그리고 그것은 할 수 있다놓다 $DBG이를 통해 최상위 함수를 호출할 수 있습니다.뒤쪽에기본 stderr 출력이 활성화됩니다.
  5. 나는 stderr의 복사본을 보관하므로 #fd>&3함수가 필요한 경우 해당 설명자에서 stderr에 명시적으로 쓸 수 있습니다.(위의 경우는 제외).

  6. 설명자는 함수를 래핑하는 복합 명령에만 연결되어 있기 때문에 fds 2, 3, 4의 현재 쉘 값에 영향을 주지 않고 스스로 닫힙니다.

    • 청소 {3,4}>&-는 필요하지 않습니다.

관련 정보