다음과 같은 단순화된 버전과 같이 소스 배열 목록에서 항목을 백업하고 있습니다.
sources=( file1 file2 "other stuff" )
backupcommand="rsync -ar ${a[@]} dest/"
su backupuser -c "$backupcommand"
$backupcommand
올바른 이스케이프를 사용하면 문제가 발생합니다. 위의 의미는 rsync가 "other stuff"라는 파일이 아닌 "other"라는 파일과 "stuff"라는 다른 파일을 찾는다는 것입니다.
인용된 명령 sh -c ...
(또는 ssh ...
무엇이든)을 어떻게 구성할 수 있습니까? 나는 배쉬를 사용하고 있습니다.
답변1
먼저, bash 배열의 내용을 표시할 방법이 필요합니다 sources
. 좋은 접근 방식 printf '%s\n' <args>
은 .NET의 각 인수에 대해 한 줄을 출력하는 것을 사용하는 것 같습니다 <args>
.
예를 들어:
$ sources=( file1 file2 "other stuff" )
$ printf '%s' "${sources[@]}"
file1
file2
other stuff
이것을 명령으로 사용하면 백업 프로그램(예를 들어)이 실제로 인수로 수신하는 내용을 backupcommand
확인할 수 있습니다 . 따라서 (or )를 통해 실행하려는 명령이 이고 두 개의 인수가 있다고 rsync
가정해 보겠습니다 .bash -c ...
ssh ...
printf '%s\n' <args>
file1
other stuff
우리는 대상 셸에서 이 명령을 실행하려고 합니다.
$ printf '%s\n' file1 other\ stuff
file1
other stuff
그러므로 이것을 논증으로 활용하기 위해서는 인용이 필요하다. 우리는 인용하기 위해 ""
or ''
or를 사용할 수 있습니다 \
. printf '%s\n'
이 작업을 성공적으로 수행했는지 확인하기 위해 다시 사용할 수 있습니다 .
$ # With backslashes:
$ printf '%s\n' printf\ \'%s\\n\'\ file1\ other\\\ stuff
printf '%s\n' file1 other\ stuff
$ # With single quotes
$ printf '%s\n' 'printf '\''%s\n'\'' file1 other\ stuff'
printf '%s\n' file1 other\ stuff
$ # Using a variable to store the command...
$ the_command='printf '\''%s\n'\'' file1 other\ stuff'
$ # ..then using that variable as a single argument...
$ printf '%s\n' "$the_command"
printf '%s\n' file1 other\ stuff
지금까지는 너무 좋았습니다. 이전 버전에서는 명령이 포함된 변수를 참조했습니다.
이제 배열을 매개변수로 사용할 수 있나요? 우리가 해야 할 일은 배열의 각 항목을 인용/이스케이프하고 단일 string 에 추가하는 것입니다 the_command
.
${var[@]}
각 배열 항목에 대해 토큰을 생성하도록 bash에 지시합니다. 그러나 이는 이미 해석되었기 때문에 이스케이프되지 않습니다. 따라서 이를 쉘 명령 문자열에 넣을 수는 없습니다. 따라서 참조를 적용하는 방법이 필요합니다.
Bash는 printf '%q' arg
인용되어야 합니다 arg
. 명령 대체를 사용할 수 있습니다.$(...)
$ sources=( file1 other\ stuff )
$ the_command=printf\ \'%s\\n\'
$ for item in "${sources[@]}"
do the_command="$the_command "$(printf '%q' "$item")
done
$ printf '%s\n' "$the_command"
printf '%s\n' file1 other\ stuff
오랫동안 살다! 그러나 다음과 같이 정리할 수 있습니다.
$ sources=( file1 other\ stuff )
$ the_command="printf '%s\\n'"$(printf ' %q' "${sources[@]}")
$ printf '%s\n' "$the_command"
printf '%s\n' file1 other\ stuff
$ # And as final proof...
$ bash -c "$the_command"
file1
other stuff
원래 질문에 적용됩니다.
sources=( file1 file2 "other stuff" )
backupcommand="rsync -ar "$(printf ' %q' "${sources[@]}")" dest/"
su backupuser -c "$backupcommand"
일반화하다
printf '%q' arg
인용 부호arg
- 명령 대체는
$(...)
명령 출력을 기반으로 단일 토큰을 생성합니다. - 다양한 인용/이스케이프 방법을 사용할 수 있습니다. 가장 읽기 쉬운 방법을 선택하세요.
답변2
아래와 같이 배열 주위에 이스케이프된 따옴표를 추가해 보세요.
backupcommand="rsync -ar \"${a[@]}\" dest/"