저는 여러 가지 이유로 상당히 이식성이 뛰어난 쉘 스크립트로 작성하려고 하는 작은 오픈 소스 프로젝트를 가지고 있습니다. 자동화된 통합 테스트는 경로 표현식의 적대적인 문자가 올바르게 처리되는지 등을 확인합니다.
/bin/sh
공급자가 제공한 사용자를 사용한 bash
테스트에서 오류가 발생했습니다 . 이를 다음과 같이 단순화했습니다.
echo "A bug\\'s life"
echo "A bug\\\\'s life"
Bash에서는 다음과 같은 예상 결과가 생성됩니다.
A bug\'s life
A bug\\'s life
내가 개발한 대시를 사용하여 다음을 수행합니다.
A bug\'s life
A bug\'s life
아직 대시에서 버그를 발견하지 못했고 뭔가 빠졌을 수도 있습니다. 이에 대한 합리적인 설명이 있습니까?
답변1
존재하다
echo "A bug\\'s life"
큰따옴표이고 \
큰따옴표 내에서 특별하기 때문에 첫 번째 것은 \
쉘에서 다음과 같이 이해됩니다.도망가다/인용하다두번째 \
. 따라서 A bug\'s life
인수가 에 전달됩니다 echo
.
echo "A bug\'s life"
똑같은 효과를 얻을 것입니다. '
큰따옴표 안에는 특별한 내용이 없으므로 \
제거되지 않으므로 전달된 매개변수와 정확히 동일합니다 echo
.
설명대로왜 printf가 echo보다 나은가요?echo
, 구현 간에는 많은 차이점이 있습니다.
UNIX 호환 구현에서는 dash
내장된 echo
가 개행 문자, 백스페이스 문자, 8진 문자 시퀀스... 및 백슬래시 자체 에 대한 \
이스케이프 시퀀스를 도입하는 데 사용됩니다 .\n
\b
\0123
\\
일부(비 POSIX)에는 -e
옵션이 필요하거나 적합성 모드에서만 이 작업을 수행합니다(예: OS/X와 같은 올바른 옵션을 사용하여 빌드할 때 또는 bash
환경에서 sh
호출할 때 ).SHELLOPTS=xpg_echo
따라서 표준(Unix 표준에만 해당, POSIX는 동작을 지정하지 않음) echo
에서
echo '\\'
그것은 다음과 같습니다:
echo "\\\\"
산출하나백슬래시( bash
일관성 모드가 아닌 경우):
echo '\\'
출력됩니다둘백슬래시.
피하고 echo
사용하는 것이 가장 좋습니다 printf
.
$ printf '%s\n' "A bug\'s life"
A bug\'s life
이 경우 모든 구현에서 동일하게 작동합니다 printf
.
dash
1은 이와 관련하여 호환되지만 UNIX 사양(POSIX + XSI)에서 요구하는 어떤 것도 출력하지 않습니다 .echo
echo -n
-n<newline>
답변2
echo 및 printf의 문제는 백틱 문자가 "특수 문자"인 경우를 이해하는 것과만 관련됩니다.
가장 간단한 것은 문자열을 사용하는 것입니다 printf '%s' "$string"
.
이 경우 처리해야 할 특수 문자가 없으며 두 번째 인수에서 printf 명령이 받는 모든 내용이 그대로 인쇄됩니다.
작은따옴표만 사용된다는 점에 유의하세요.
$ printf '%s\n' '\\\\\\\\\T ' # nine \
\\\\\\\\\T # nine \
문자열이 첫 번째 인수로 사용되는 경우 특정 문자는 특수합니다.
쌍 \\
은 단일 \
및 \T
단수를 나타냅니다 T
.
$ printf '\\\\\\\\\T ' # nine \
\\\\T # four \
네 쌍 각각은 \\
단일 로 변환되고 \
, 마지막 \T
쌍은 단일 로 변환됩니다 T
.
$ printf '\\\\\\\\\a ' # nine \
\\\\ # four \
4개의 쌍 각각은 \\
단일 문자로 변환되며 \
마지막 문자는 \a
벨(BEL) 문자(인쇄할 수 없음)로 변환됩니다.
에서도 같은 상황이 발생합니다일부에코 구현.
대시 구현은 항상 특수 백슬래시 문자를 변환합니다.
이 코드를 스크립트에 넣으면:
set -- '\g ' '\\g ' '\\\g ' '\\\\g ' '\\\\\g ' '\\\\\\g ' '\\\\\\\g ' '\\\\\\\\g ' '\\\\\\\\\g '
for i ; do
printf '<%-14s> \t<%-9s> \t<%-14s> \t<%-12s>\n' \
"$(printf '%s ' "|$i|")" \
"$(printf "|$i|")" \
"$(echo "|$i|")" \
"$(echo -e "|$i|")" ;
done
그런 다음 대시가 인쇄됩니다( dash ./script
):
<|\g | > <|\g | > <|\g | > <-e |\g | >
<|\\g | > <|\g | > <|\g | > <-e |\g | >
<|\\\g | > <|\\g | > <|\\g | > <-e |\\g | >
<|\\\\g | > <|\\g | > <|\\g | > <-e |\\g | >
<|\\\\\g | > <|\\\g | > <|\\\g | > <-e |\\\g | >
<|\\\\\\g | > <|\\\g | > <|\\\g | > <-e |\\\g | >
<|\\\\\\\g | > <|\\\\g | > <|\\\\g | > <-e |\\\\g | >
<|\\\\\\\\g | > <|\\\\g | > <|\\\\g | > <-e |\\\\g | >
<|\\\\\\\\\g | > <|\\\\\g |> <|\\\\\g | > <-e |\\\\\g |>
처음 두 열은 모든 쉘(printf)에 대해 동일합니다.
나머지 두 개는 사용된 에코의 특정 구현에 따라 달라집니다.
예: ash ./script
(Busybox 회색):
<|\g | > <|\g | > <|\g | > <|\g | >
<|\\g | > <|\g | > <|\\g | > <|\g | >
<|\\\g | > <|\\g | > <|\\\g | > <|\\g | >
<|\\\\g | > <|\\g | > <|\\\\g | > <|\\g | >
<|\\\\\g | > <|\\\g | > <|\\\\\g | > <|\\\g | >
<|\\\\\\g | > <|\\\g | > <|\\\\\\g | > <|\\\g | >
<|\\\\\\\g | > <|\\\\g | > <|\\\\\\\g | > <|\\\\g | >
<|\\\\\\\\g | > <|\\\\g | > <|\\\\\\\\g | > <|\\\\g | >
<|\\\\\\\\\g | > <|\\\\\g |> <|\\\\\\\\\g | > <|\\\\\g | >
사용된 문자가 이면 a
대시를 나타냅니다.
<|\a | > <| | > <| | > <-e | | >
<|\\a | > <|\a | > <|\a | > <-e |\a | >
<|\\\a | > <|\ | > <|\ | > <-e |\ | >
<|\\\\a | > <|\\a | > <|\\a | > <-e |\\a | >
<|\\\\\a | > <|\\ | > <|\\ | > <-e |\\ | >
<|\\\\\\a | > <|\\\a | > <|\\\a | > <-e |\\\a | >
<|\\\\\\\a | > <|\\\ | > <|\\\ | > <-e |\\\ | >
<|\\\\\\\\a | > <|\\\\a | > <|\\\\a | > <-e |\\\\a | >
<|\\\\\\\\\a | > <|\\\\ | > <|\\\\ | > <-e |\\\\ | >
배쉬의 경우:
<|\a | > <| | > <|\a | > <| | >
<|\\a | > <|\a | > <|\\a | > <|\a | >
<|\\\a | > <|\ | > <|\\\a | > <|\ | >
<|\\\\a | > <|\\a | > <|\\\\a | > <|\\a | >
<|\\\\\a | > <|\\ | > <|\\\\\a | > <|\\ | >
<|\\\\\\a | > <|\\\a | > <|\\\\\\a | > <|\\\a | >
<|\\\\\\\a | > <|\\\ | > <|\\\\\\\a | > <|\\\ | >
<|\\\\\\\\a | > <|\\\\a | > <|\\\\\\\\a | > <|\\\\a | >
<|\\\\\\\\\a | > <|\\\\ | > <|\\\\\\\\\a | > <|\\\\ | >
이를 위해서는 명령을 실행하는 셸이 문자열에서도 작동할 수 있다는 설명을 추가해야 합니다.
$ printf '%s\n' '\\\\T '
\\\\T
$ printf '%s\n' "\\\\T "
\\T
쉘은 큰따옴표 안에 백슬래시를 사용하여 작업을 수행합니다.
이 코드를 사용하면:
tab=' '
say(){ echo "$(printf '%s' "$a") $tab $(echo "$a") $tab $(echo -e "$a")"; }
a="one \a " ; say
a="two \\a " ; say
a="t33 \\\a " ; say
a="f44 \\\\a " ; say
a="f55 \\\\\a " ; say
a="s66 \\\\\\a " ; say
a="s77 \\\\\\\a " ; say
a="e88 \\\\\\\\a " ; say
a="n99 \\\\\\\\\a " ; say
두 가지 효과가 모두 추가되어 다음과 같은 결과를 얻습니다.
$ bash ./script
one \a one \a one
two \a two \a two
t33 \\a t33 \\a t33 \a
f44 \\a f44 \\a f44 \a
f55 \\\a f55 \\\a f55 \
s66 \\\a s66 \\\a s66 \
s77 \\\\a s77 \\\\a s77 \\a
e88 \\\\a e88 \\\\a e88 \\a
n99 \\\\\a n99 \\\\\a n99 \\
대시의 경우 상황은 더욱 심각합니다.
$ dash ./script
one one -e one
two two -e two
t33 \a t33 -e t33
f44 \a f44 -e f44
f55 \ f55 \ -e f55 \
s66 \ s66 \ -e s66 \
s77 \\a s77 \a -e s77 \a
e88 \\a e88 \a -e e88 \a
n99 \\ n99 \ -e n99 \