$BASH_COMMAND를 평가하는 것이 안전합니까?

$BASH_COMMAND를 평가하는 것이 안전합니까?

저는 변수로부터 복잡한 명령을 구성하는 쉘 스크립트를 작업 중입니다. 예를 들어 다음과 같습니다.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이 명령을 변수에 넣어 시스템 로그에 기록할 수도 있습니다.

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}"

잘 처리된 것 같지만 제가 놓친 문제를 누군가가 본다면 궁금합니다.

답변1

한 가지 가능성은 다음과 같이 명령을 인쇄하고 동시에 실행하는 래퍼 함수를 ​​만드는 것입니다.

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

답변2

eval "$BASH_COMMAND"명령을 실행하십시오.

printf '%s\n' "$BASH_COMMAND"개행 문자와 함께 지정된 정확한 명령을 인쇄합니다.

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

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

관련 정보