bash
다음으로 구성된 문자열을 포함하는 쉘 변수가 있습니다.성격공백으로 구분됩니다. 문자열에는 단어 내의 이스케이프 공백과 같은 이스케이프 문자가 포함될 수 있습니다. 공백이 포함된 단어도 인용할 수 있습니다.
$FOO
대신 따옴표 없이 사용된 쉘 변수는 "$FOO"
여러 단어가 되지만 원래 문자열의 따옴표와 이스케이프는 효과가 없습니다.
인용문과 이스케이프 문자를 고려하여 문자열을 단어로 분할하는 방법은 무엇입니까?
배경
서버는 클라이언트에 제공되는 명령줄에 관계없이 스크립트에 대한 액세스를 제한하기 위해 ssh
파일 ForceCommand
에 옵션을 제공합니다 .sshd_config
ssh
스크립트는 계속하기 전에 변수 ( 클라이언트에 제공되는 명령줄이 SSH_ORIGINAL_COMMAND
포함된 문자열 )를 사용하여 인수 목록을 설정합니다. 그래서 사용자는ssh
ssh
$ ssh some_server foo 'bar car' baz
스크립트가 실행되는 것을 확인하고 스크립트가 실행되면 SSH_ORIGINAL_COMMAND
4개의 매개변수로 설정됩니다.foo bar car baz
set -- ${SSH_ORIGINAL_COMMAND}
원하는 결과가 아닙니다. 따라서 사용자는 다시 시도합니다.
$ ssh some_server foo bar\ car baz
동일한 결과 - 클라이언트 셸이 이를 보려면 두 번째 매개변수의 백슬래시를 이스케이프해야 합니다 ssh
. 이것들은 어떻습니까?
$ ssh some_server foo 'bar\ car' baz
$ ssh some_server foo bar\\ car baz
둘 다 유효합니다.printf "%q"
참조 래퍼클라이언트 참조가 단순화될 수 있습니다.
클라이언트 인용을 사용하면 적절하게 인용된 문자열이 서버에 전송되어 ssh
서버에서 수신한 SSH_ORIGINAL_COMMAND
백슬래시가 그대로 유지됩니다 foo bar\ car baz
.
set
하지만 인용이나 이스케이프를 고려하지 않기 때문에 여전히 문제가 있습니다 . 해결책이 있습니다:
eval set -- ${SSH_ORIGINAL_COMMAND}
그러나 이것은 용납될 수 없습니다. 고려하다
$ ssh some_server \; /bin/sh -i
매우 불만족: eval
입력 제어가 부족하여 사용할 수 없습니다.
eval
실행 부분이 없는 문자열 확장 기능 이 필요합니다 .
답변1
사용 read
:
read -a ssh_args <<< "${SSH_ORIGINAL_COMMAND}"
set -- "${ssh_args[@]}"
이는 단어를 SSH_ORIGINAL_COMMAND
배열로 구문 분석 ssh_args
하고 \
백슬래시( )를 이스케이프 문자로 처리합니다. 그러면 배열 요소가 매개변수로 제공됩니다 set
. ssh
다음과 같이 전달된 매개변수 목록 과 함께 작동합니다 .
$ ssh some_server foo 'bar\ car' baz
$ ssh some_server foo bar\\ car baz
ㅏprintf "%q"는 SSH 래퍼를 나타냅니다.다음은 허용됩니다.
$ sshwrap some_server foo bar\ car baz
$ sshwrap some_server foo 'bar car' baz
다음은 이러한 래퍼의 예입니다.
#!/bin/bash
h=$1; shift
QUOTE_ARGS=''
for ARG in "$@"
do
ARG=$(printf "%q" "$ARG")
QUOTE_ARGS="${QUOTE_ARGS} $ARG"
done
ssh "$h" "${QUOTE_ARGS}"
답변2
문자열을 인용하는 방법:
${parameter@operator}
섹션을 참조하세요 .https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
- 단일 변수의 경우:
"${var@Q}"
a
-> 'a'
, a'b
-> 'a'\''b'
.
- 배열의 경우
"@{array[@]@Q}"
배열의 각 요소는 따옴표로 묶인 다음 공백을 사용하여 하나의 큰 문자열로 연결됩니다.
- 프로그램 매개변수의 경우
$@
"${@@Q}"
(Bash 4.4+가 필요할 수 있습니다. 사용할 수 없는 경우 printf "%q"를 사용할 수 있지만 배열의 각 요소를 참조하는 기능이 손실됩니다)
인용된 문자열을 다시 배열로 역참조하는 방법
@eel ghEEz가 지적한 안전한 방법 1개만 찾았습니다.
declare -a array="($QUOTED_ARGS)"
2022/08/31 편집: 명령 삽입을 방지하려면 인용된 인수를 전달하는 것이 중요합니다.
더 정확하게는 그래야 한다.
JOINED_ARGMENTS_STRING="......"
declare -a array="(${JOINED_ARGMENTS_STRING@Q})"
견본:
cat <<'EOF' > show_args
#!/bin/bash
for arg in "$@"; do
echo "ARG_$((++i))=$arg"
done
EOF
chmod +x show_args
cat <<'EOF' > test.sh
#!/bin/bash
QUOTED_ARGS=${@@Q} # this is important!!!!!!
echo QUOTED_ARGS is "$QUOTED_ARGS"
echo de-quote QUOTED_ARGS
declare -a args="($QUOTED_ARGS)"
./show_args "${args[@]}"
EOF
chmod +x test.sh
시험:
ARGS=("a a a" "b'b'b" 'c"c"c')
./show_args "${ARGS[@]}"
3개의 요소로 구성된 배열임을 보여줄 수 있습니다.
ARG_1=a a a
ARG_2=b'b'b
ARG_3=c"c"c
따옴표와 백틱이 어떻게 작동하는지 살펴보겠습니다.
./test.sh "${ARGS[@]}"
또는
./test.sh "a a a" "b'b'b" 'c"c"c'
밝혀지다
QUOTED_ARGS is 'a a a' 'b'\''b'\''b' 'c"c"c'
de-quote QUOTED_ARGS
ARG_1=a a a
ARG_2=b'b'b
ARG_3=c"c"c
2022/08/31 편집: 주입 시도 테스트 추가:
./test.sh "\$(echo test >&2)"
결과:
QUOTED_ARGS is '$(echo test >&2)'
de-quote QUOTED_ARGS
ARG_1=$(echo test >&2)
"echo test" 명령이 호출되지 않아 좋습니다.
성공적으로 복구되었습니다.