Bash

bash 可以將帶引號和/或轉義的字元串變數擴展為單詞嗎?

  • February 19, 2022

我有一個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}"

引用自:https://unix.stackexchange.com/questions/324929