Bash는 인용 및/또는 이스케이프된 문자열 변수를 단어로 확장할 수 있나요?

Bash는 인용 및/또는 이스케이프된 문자열 변수를 단어로 확장할 수 있나요?

bash다음으로 구성된 문자열을 포함하는 쉘 변수가 있습니다.성격공백으로 구분됩니다. 문자열에는 단어 내의 이스케이프 공백과 같은 이스케이프 문자가 포함될 수 있습니다. 공백이 포함된 단어도 인용할 수 있습니다.

$FOO대신 따옴표 없이 사용된 쉘 변수는 "$FOO"여러 단어가 되지만 원래 문자열의 따옴표와 이스케이프는 효과가 없습니다.

인용문과 이스케이프 문자를 고려하여 문자열을 단어로 분할하는 방법은 무엇입니까?

배경

서버는 클라이언트에 제공되는 명령줄에 관계없이 스크립트에 대한 액세스를 제한하기 위해 ssh파일 ForceCommand에 옵션을 제공합니다 .sshd_configssh

스크립트는 계속하기 전에 변수 ( 클라이언트에 제공되는 명령줄이 SSH_ORIGINAL_COMMAND포함된 문자열 )를 사용하여 인수 목록을 설정합니다. 그래서 사용자는sshssh

$ ssh some_server foo 'bar car' baz

스크립트가 실행되는 것을 확인하고 스크립트가 실행되면 SSH_ORIGINAL_COMMAND4개의 매개변수로 설정됩니다.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" 명령이 호출되지 않아 좋습니다.

성공적으로 복구되었습니다.

관련 정보