인용되지 않은 문자열 확장을 사용하여 tar에 두 개의 인수를 전달하려고 합니다. 첫 번째 인수는 명령줄 플래그 --exclude
이고 두 번째 인수는 *
문자를 포함합니다. 조기 와일드카드를 피하기 위해 다음을 인용해 보았습니다 *
.
shopt -s nullglob
x="--exclude '*'"
echo tar $x
놀랍게도 '*'
완전히 사라졌습니다! 출력은 다음과 같습니다.
tar --exclude
POSIX 셸에서 Bash로 전환하고 배열을 사용하면 이 문제를 피할 수 있다는 것을 알고 있습니다. 하지만 나는 여전히 알고 싶습니다. POSIX 셸 조각에 도대체 무슨 일이 벌어지고 있는 걸까요? 참조된 glob을 삭제하는 이유는 무엇입니까? 인용된 부분을 그대로 유지하거나, 쭉 확장해서 글로벌하게 확장할 것이라고 생각했을 것입니다. 하지만 둘 다 그렇지 않습니다.
답변1
첫째, 이미 사용하고 있거나 bash
적어도 POSIX가 아닌 옵션 중 일부를 사용하고 있습니다. ( POSIX 도 마찬가지 shopt
입니다 nullglob
.)
이제 무슨 일이 일어나고 있는지 설명하겠습니다.
x="--exclude '*'"
echo tar $x
이 변수는 $x
공백을 토큰화하고 결과 부분을 글로빙에 사용합니다. --exclude
와일드카드가 없으며 그대로 유지됩니다. '*'
일치하는 항목이 없습니다(파일 이름이 문자 그대로 작은따옴표로 시작하고 끝나지 않는 한). 와일드카드가 포함되어 있고 nullglob
삭제하도록 설정했기 때문입니다. 밝혀지다
tar --exclude
이미 사용하고 계시기 때문에 bash
한번 사용해보시는 것도 좋을 것 같습니다.
x=('--exclude' '*')
echo tar "${x[@]}"
POSIX 솔루션을 원한다면 제가 제공할 수 있는 최선의 방법은 기본 매개변수 목록을 재사용하는 것입니다.
set -- '--exclude' '*'
echo tar "$@"
답변2
이와 같은 코너 케이스의 경우 쉘 확장 순서배쉬 문서예상치 못한 동작이 발생하는 경우가 많습니다. Posix는 거의 동일한 동작을 가지고 있지만 덜 명확하게 설명되어 있습니다.
일어나는 일은 다음과 같습니다.
nullglob을 nullglob로 변환했으므로 실패한 glob은 비어 있습니다.
다음으로 리터럴 별표와 작은따옴표가 포함된 변수를 설정합니다.
그런 다음 변수의 내용을 에코하면 다음이 발생합니다.
- 변수가 텍스트로 확장됩니다. 텍스트에는
'*'
'*'
작은따옴표로 인해 문자열 리터럴이 될 것이라고 생각하더라도 기존 파일 이름은 여전히 대체됩니다. Nullglob은 이를 빈 문자열로 변환합니다.- 명령이 실행됩니다