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
同じ結果 - クライアントシェルがこれを表示するには、2番目のパラメータのバックスラッシュをエスケープする必要があります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」コマンドが呼び出されないことをお勧めします。
正常に修復されました。