bash 可以將帶引號和/或轉義的字元串變數擴展為單詞嗎?
我有一個shell 變數,其中包含一個由空格分隔
bash
的多個*單片語成的字元串。*字元串可以包含轉義,例如單詞中的轉義空格。包含空格的單詞也可以被引用。使用不帶引號(
$FOO
而不是"$FOO"
)的 shell 變數會變成多個單詞,但原始字元串中的引號和轉義無效。考慮到引號和轉義字元,如何將字元串拆分為單詞?
背景
ssh
伺服器通過使用文件中的ForceCommand
選項提供受限訪問sshd_config
以強制執行腳本,而不管提供給ssh
客戶端的命令行如何。該腳本使用變數
SSH_ORIGINAL_COMMAND
(它是一個字元串,由 設置ssh
,包含提供給ssh
客戶端的命令行)在繼續之前設置其參數列表。所以,一個使用者在做$ ssh some_server foo 'bar car' baz
將看到腳本執行,並且當腳本執行時它將
SSH_ORIGINAL_COMMAND
設置為foo bar car baz
哪個將成為四個參數set -- ${SSH_ORIGINAL_COMMAND}
不是想要的結果。所以使用者再次嘗試:
$ ssh some_server foo bar\ car baz
相同的結果 - 第二個參數中的反斜杠需要為客戶端的 shell 進行轉義,以便
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
沒有執行部分的字元串擴展能力。
使用
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}"