나는 왜 이 세 명령이 세 가지 다른 답을 주는지 물었습니다.
$ printf "%s\n" `echo ../.. | sed 's/[.]/\\&/g'`
&&/&&
$ printf "%s\n" $(echo ../.. | sed 's/[.]/\\&/g')
../..
$ printf "%s\n" "$(echo ../.. | sed 's/[.]/\\&/g')"
\.\./\.\.
답변1
이것은 대답하기 어려운 질문입니다. 마지막 설명의 가장 간단한 예입니다.
전체 견적
실제로 완전히 인용된 예는 다음과 같습니다.
$ printf "%s\n" "$( echo "../.." | sed 's/[.]/\\&/g' )"
\.\./\.\.
모든 것이 인용되기 때문에 쉘은 여기서 어떤 트릭이나 변경도 수행하지 않습니다. 가장 안쪽 내용은 echo "../.."
인용되므로 파일 이름 확장의 영향을 받지 않습니다. 변경되지 않은 채 sed로 이동하고 sed는 각 점을 로 변경합니다 \&
. 그런 다음 명령의 결과도 인용되어 "$(...)"
(다시) 쉘에 대한 변경을 방지하고 printf
명령도 \.\./\.\.
여기에 을 인쇄합니다.
로 변경 ##/##
파일 이름 확장자(와일드카드)가 있으면 ../..
어쨌든 ../..
. 따라서 최종 문자열은 동일합니다. 하지만 이 문제를 테스트해 보겠습니다.
$ echo ../../t*
../../test2.txt ../../tested ../../test.txt
$ set -f # remove all filename expansion
$ echo ../../t* ../..
../../t* ../..
*
그리고 어떤 경우에도 ?
a , a 또는 a 를 포함하지 않는 문자열은 [
다음의 영향을 받지 않습니다.패턴 일치 표기법
증명: GLOBIGNORE 설정
$ (GLOBIGNORE='.*:..*'; echo "any" ../../t* ../.. "value")
any ../.. value
globbing(파일 이름 확장)의 영향을 받은 경우 ../..
GLOBIGNORE 값으로 인해 제거됩니다.
그러나 파일 이름 확장자가 없는지 확인하기 위해 존재하지 않는 파일 이름으로 전환할 수 있습니다.##/##
따옴표 없이 명령 실행
\
따옴표 없이 (명령이 실행되는 경우에도) 쉘이 a 를 제거할 이유가 없습니다 .
$ printf '%s\n' $(printf '%s\n' '\#\#/\#\#')
\#\#/\#\#
사실, 제가 테스트한 쉘 중 어느 것도 두 번째 예에서 보고한 내용을 보여주지 않았습니다. (수정해주세요!)
편집: bash 5.0.17에 버그가 있습니다. 그러나 포인트에서만.
$ b50sh
$ echo $BASH_VERSION
5.0.17(3)-release
$ printf '%s\n' $(printf '%s\n' '\.\./\.\.')
../..
$ printf '%s\n' $(printf '%s\n' '\#\#/\#\#')
\#\#/\#\#
$ printf '%s\n' $(printf '%s\n' '\>\>/\>\>')
\>\>/\>\>
$ exit
$ echo $BASH_VERSION
5.1.4(1)-release
$ printf '%s\n' $(printf '%s\n' '\.\./\.\.')
\.\./\.\.
$ printf '%s\n' $(printf '%s\n' '\#\#/\#\#')
\#\#/\#\#
bash 5.1.4에서 해결된 것 같습니다.
백틱
이것은 가장 어려운 문제입니다. 내부의 모든 백틱은 \\
가 됩니다 \
.
따라서 sed 명령(sed에 표시된 대로)은 다음과 같습니다 s/[#]/\&/g
. 내 생각에 당신이 의미하는 바를 수행하려면 s를 복사해야합니다 \
.
$ printf '%s\n' `printf '%s\n' '##/##' | sed 's/[#]/\\&/g'`
&&/&&
$ printf '%s\n' "`printf '%s\n' '##/##' | sed 's/[#]/\\&/g'`"
&&/&&
$ printf '%s\n' "`printf '%s\n' '##/##' | sed 's/[#]/\\\\&/g'`"
\#\#/\#\#
답변2
Bash 버전 5.0.17에서는 man bash
두 가지 명령 대체 형식 사이에 다음과 같은 차이점이 있습니다.
When the old-style backquote form of substitution is used, backslash retains its literal meaning except when followed by $, `, or \. The first backquote not preceded by a backslash terminates the command sub‐ stitution. When using the $(command) form, all characters between the parentheses make up the command; none are treated specially.
set -x
쉘에 있다면 차이점을 볼 수 있습니다:
$ set -x
$ printf "%s\n" `echo ../.. | sed 's/[.]/\\&/g'`
++ echo ../..
++ sed 's/[.]/\&/g'
+ printf '%s\n' '&&/&&'
&&/&&
디스플레이가 \\&
다음과 같이 축소되었습니다 \&
.\
예\
); 는 sed &
에서 특별한 의미를 잃어서 , while(새로운 스타일) ..
이 되었습니다.&&
$ printf "%s\n" $(echo ../.. | sed 's/[.]/\\&/g')
++ echo ../..
++ sed 's/[.]/\\&/g'
+ printf '%s\n' ../..
../..
두 백슬래시는 문자 그대로 sed에 전달되며, sed는 이를 ..
( 문자 그대로의 백슬래시이며 \.\.
특별한 의미를 유지함)로 대체합니다.\\
&
인용되지 않음그런 다음 결과는 쉘에 의해 반영되고 ../..
인용된 명령 대체가 sed 출력을 그대로 인쇄하면 다음을 얻습니다 \.\./\.\.
.
$ printf "%s\n" "$(echo ../.. | sed 's/[.]/\\&/g')"
++ echo ../..
++ sed 's/[.]/\\&/g'
+ printf '%s\n' '\.\./\.\.'
\.\./\.\.
당신은 또한 볼 수 있습니다...
$(...)가 (backtick) 보다 나은 이유는 무엇입니까 ?
Bash 버전 4.4.20에서는 인용되지 않은 버전과 인용된 명령 대체 버전이 $(...)
동일한 결과를 제공합니다.
$ echo $BASH_VERSION
4.4.20(1)-release
$ set -x
$ printf "%s\n" $(echo ../.. | sed 's/[.]/\\&/g')
++ echo ../..
++ sed 's/[.]/\\&/g'
+ printf '%s\n' '\.\./\.\.'
\.\./\.\.