저는 변수로부터 복잡한 명령을 구성하는 쉘 스크립트를 작업 중입니다. 예를 들어 다음과 같습니다.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/script
set -x