변수와 같은 복잡한 명령을 구성하는 쉘 스크립트를 작성 중입니다 (예 : 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
쉘 내부에서 호출 하여 쉘 옵션 을 설정하는 것 입니다. 추적이 표준 오류로 인쇄됩니다.