$ BASH_COMMAND를 평가하는 것이 안전합니까? ${ANOTHER_ARG:+–with “$ANOTHER_ARG”} 이

변수와 같은 복잡한 명령을 구성하는 쉘 스크립트를 작성 중입니다 (예 : Bash FAQ에서 배운 기술 사용 ).

#!/bin/bash

SOME_ARG="abc"
ANOTHER_ARG="def"

some_complex_command \
  ${SOME_ARG:+--do-something "$SOME_ARG"} \
  ${ANOTHER_ARG:+--with "$ANOTHER_ARG"}

이 스크립트는 동적 매개 변수를 추가 --do-something "$SOME_ARG"하고 --with "$ANOTHER_ARG"some_complex_command이러한 변수가 정의 된 경우. 지금까지 이것은 잘 작동합니다.

그러나 이제는 명령을 실행할 때 (예 : 스크립트가 디버그 모드에서 실행될 때) 명령을 인쇄하거나 기록 할 수 있기를 원합니다. 따라서 스크립트가 실행될 some_complex_command --do-something abc --with def때이 명령을 변수 안에 넣고 syslog에 기록 할 수 있습니다.

Bash FAQ는 이 목적으로 트랩과 변수 (예 : 디버깅 목적) 를 사용DEBUG$BASH_COMMAND 하는 기술을 보여줍니다 . 다음 코드를 사용하여 시도했습니다.

#!/bin/bash

ARG="test string"

trap 'COMMAND="$BASH_COMMAND"; trap - DEBUG' DEBUG
echo "$ARG"

echo "Command was: ${COMMAND}"

이것은 작동하지만 명령의 변수를 확장하지는 않습니다.

host ~ # ./test.sh
test string
Command was: echo "$ARG"

나는 eval을 사용하여 확장해야한다고 생각 echo "$ARG"합니다 echo test string(적어도 eval아직 없는 방법은 찾지 못했습니다). 다음은 작동합니다.

eval echo "Command was: ${COMMAND}"

다음과 같은 출력을 생성합니다.

host ~ # ./test.sh
test string
Command was: echo "$ARG"
Command was: echo test string

그러나 eval이와 같이 안전하게 사용할 수 있는지 확실하지 않습니다 . 나는 몇 가지를 악용하려고 시도했습니다.

#!/bin/bash

ARG="test string; touch /x"
DANGER='$(touch /y; cat /etc/shadow)'

trap 'COMMAND="$BASH_COMMAND"; trap - DEBUG' DEBUG
echo "$ARG" $DANGER

echo "Command was: ${COMMAND}"
eval echo "Command was: ${COMMAND}"

잘 처리하는 것 같지만 다른 사람이 내가 놓친 문제를 발견하면 궁금합니다.



답변

한 가지 가능성은 다음과 같이 명령을 인쇄하고 실행하는 랩퍼 기능을 작성하는 것입니다.

debug() {
    # This function prints (to stdout) its arguments and executes them
    local args=() idx=0 IFS=' ' c
    for c; do printf -v args[idx++] '%q' "$c"; done
    printf "%s\n" "${args[*]}"
    # Execute!
    "$@"
}

따라서 스크립트에서 다음을 수행 할 수 있습니다.

debug echo "$ARG"

함정을 피할 필요가 없습니다. 단점은 debug코드 전체에 일부 키워드를 추가한다는 것입니다 (그러나 괜찮습니다. 어설 션 등과 같은 것들이 일반적입니다).

전역 변수를 추가 DEBUG하고 debug다음과 같이 함수를 수정할 수도 있습니다 .

debug() {
    # This function prints (to stdout) its arguments if DEBUG is non-null
    # and executes them
    if [[ $DEBUG ]]; then
        local args=() idx=0 IFS=' ' c
        for c; do printf -v args[idx++] '%q' "$c"; done
        printf "%s\n" "${args[*]}"
    fi
    # Execute!
    "$@"
}

그런 다음 스크립트를 다음과 같이 호출 할 수 있습니다.

$ DEBUG=yes ./myscript

또는

$ DEBUG= ./myscript

아니면 그냥

$ ./myscript

디버그 정보를 원하는지 여부에 따라 다릅니다.

DEBUG환경 변수로 취급해야하기 때문에 변수를 대문자 로 표시했습니다. DEBUG사소하고 일반적인 이름이므로 다른 명령과 충돌 할 수 있습니다. 아마 전화 GNIOURF_DEBUG또는 MARTIN_VON_WITTICH_DEBUG또는 UNICORN_DEBUG당신이 만약 유니콘 같은 (다음 아마도 조랑말 너무 좋아).

노트. debug함수 printf '%q'에서 출력을 직접 이스케이프하고 인용하여 직접 복사하여 붙여 넣을 수있는 그대로 사용할 수 있도록 각 인수의 형식을 신중하게 지정했습니다 . 또한 공백이나 다른 재미있는 기호의 경우 각 인수를 알아낼 수있을 때 쉘에서 본 내용을 정확하게 보여줍니다. 이 기능은 또한 불필요한 서브 쉘을 피하기 위해 -v스위치로 직접 할당을 사용합니다 printf.


답변

eval "$BASH_COMMAND" 명령을 실행합니다.

printf '%s\n' "$BASH_COMMAND" 정확하게 지정된 명령과 개행을 인쇄합니다.

명령에 변수가 포함 된 경우 (예 : cat "$foo" ) 명령을 인쇄하면 변수 텍스트가 인쇄됩니다. 명령을 실행하지 않고 변수 값을 인쇄하는 것은 불가능합니다 variable=$(some_function) other_variable=$variable.

쉘 스크립트를 실행하여 추적을 얻는 가장 간단한 방법 xtrace은 스크립트를 실행 bash -x /path/to/script하거나 set -x쉘 내부에서 호출 하여 쉘 옵션 을 설정하는 것 입니다. 추적이 표준 오류로 인쇄됩니다.


답변