단어 분리를 방지하기 위해 문자열 내에서 변수 확장을 인용하는 방법은 무엇입니까?

단어 분리를 방지하기 위해 문자열 내에서 변수 확장을 인용하는 방법은 무엇입니까?
$ myvar="/path to/my directory"
$ sudo bash -c "cd $myvar"

이 경우 $myvar값의 공백으로 인한 토큰화를 방지하려면 어떻게 인용해야 합니까 myvar?

답변1

아니요분사(따옴표가 없는 확장 시 변수를 분할하는 기능과 유사) 해당 코드에서는 as가 $myvar인용되지 않습니다.

하지만 $myvar. bash​따라서 그 내용은 bash 코드로 해석됩니다!

거기에 있는 공백으로 인해 여러 인수가 에 전달될 수 있습니다 cd.분사, 그러나 쉘 구문에서 여러 토큰으로 구문 분석되기 때문입니다. 값이 이면 bye;reboot재시작이 발생합니다!

원하는 곳은 다음과 같습니다.

sudo bash -c 'cd -P -- "$1"' bash "$myvar"

$myvar( 이 인라인 스크립트에 첫 번째 인수로 전달하는 내용 . IFS 토큰화(및 와일드카드)를 방지하기 위해 해당 쉘에 $myvar및 가 어떻게 인용되어 있는지 참고하세요.)$1

또는:

sudo MYVAR="$myvar" bash -c 'cd -P -- "$MYVAR"'

$myvar(환경 변수에 전달하는 것).

물론 다음을 실행해도 유용한 것은 없습니다.오직 cd인라인 스크립트에서( 들어가는 root것이 가능한지 확인하는 것 외에도 ) 아마도 당신은 그 스크립트를 거기에 두고 다음과 같은 다른 일을 하고 cd싶을 것입니다 :cd

sudo bash -c 'cd -P -- "$1" && do-something' bash "$myvar"

접근할 수 없는 디렉토리 sudo에 접근 하는 것이 목적이라면 실제로는 작동하지 않습니다.cd

sudo sh -c 'cd -P -- "$1" && exec bash' sh "$myvar"

bash현재 디렉토리와 상호 작용을 시작합니다 $myvar. 그러나 쉘은 root.

다음을 수행할 수 있습니다.

sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"

bash현재 디렉터리와 비특권으로 상호 작용하려는 경우, $myvar처음에 해당 디렉터리에 들어갈 수 있는 권한이 없으면 해당 디렉터리가 현재 작업 디렉터리이더라도 해당 디렉터리에서 아무 것도 할 수 없습니다.cd

$ myvar=/var/spool/cron/crontabs
$ sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
bash-4.4$ ls
ls: cannot open directory '.': Permission denied

예외는 다음과 같습니다.찾다디렉터리 자체에 액세스할 수 있지만 해당 경로의 디렉터리 구성 요소 중 하나에는 액세스할 수 없습니다.

$ myvar=1/2
$ mkdir -p "$myvar"
$ chmod 0 1
$ cd 1/2
cd: permission denied: 1/2
$ sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
bash-4.4$ pwd
/home/stephane/1/2
bash-4.4$ mkdir 3
bash-4.4$ ls
3
bash-4.4$ cd "$PWD"
bash: cd: /home/stephane/1/2: Permission denied

엄밀히 말하면 $myvarlike (문자 그대로) 값의 경우 $(seq 10)쉘이 해당 명령 대체를 확장할 때 단어 분리기가 발생합니다.bashroot

답변2

새로운(bash 4.4) 인용 마법을 사용하면 원하는 작업을 보다 직접적으로 수행할 수 있습니다. 더 큰 상황에 따라 다른 기술 중 하나를 사용하는 것이 더 나을 수도 있지만 제한된 상황에서는 작동합니다.

sudo bash -c "cd ${myvar@Q}; pwd"

답변3

콘텐츠를 신뢰할 수 없는 경우 $myvar유일한 합리적인 접근 방식은 GNU를 사용하여 printf콘텐츠의 이스케이프 버전을 만드는 것입니다.

#!/bin/bash

myvar=$(printf '%q' "$myvar")
bash -c "echo $myvar"

printf매뉴얼 페이지에 따르면 다음 과 같습니다 .

%q

$''ARGUMENT 인쇄할 수 없는 문자를 이스케이프하기 위해 권장되는 POSIX 구문을 사용하여 쉘 입력으로 재사용할 수 있는 형식으로 인쇄합니다 .

답변4

Stéphane Chazelas가 제안하는 것은 확실히 이를 수행하는 가장 좋은 방법입니다. sedR..의 답변과 같이 하위 프로세스를 사용하는 대신 매개 변수 확장 구문 참조를 안전하게 사용하는 방법에 대한 답변을 제공하고 있습니다 .

myvar='foo \'\''"bar'

quote() {
  printf "'%s'" "${1//\'/\'\\\'\'}"
}

printf "value: %s\n" "$myvar"
printf "quoted: %s\n" "$(quote "$myvar")"
bash -c "printf 'interpolated in subshell script: %s\n' $(quote "$myvar")"

이 스크립트의 출력은 다음과 같습니다.

value: foo \'"bar
quoted: 'foo \'\''"bar'
interpolated in subshell script: foo \'"bar

관련 정보