[英]Double quotes inside quotes in bash
我需要將$ var_path傳遞到單引號內的bash腳本,然后在遠程主機上執行commnd。 我知道單引號不允許傳遞變量,因此嘗試了雙引號,但在sed中出錯。 假設發生這種情況是因為其模板也使用了“”。
var="Test text"
var_path="/etc/file.txt"
echo "$var"|ssh root@$host 'cat - > /tmp/test.tmp && sed -n "/]/{:a;n;/}/b;p;ba}" $var_path > /tmp/new.conf.tmp'
所以用“”
var="Test text"
var_path="/etc/file.txt"
echo "$var"|ssh root@$host "cat - > /tmp/test.tmp && sed -n "/]/{:a;n;/}/b;p;ba}" $var_path > /tmp/new.conf.tmp"
sed錯誤
sed: -e expression #1, char 0: unmatched `{'
./script.sh: line 4: n: command not found
./script.sh: line 4: /}/b: No such file or directory
./script.sh: line 4: p: command not found
如果直接使用$ var_path而不進行替換,腳本將按預期工作。
解析為要發送到SSH中遠程系統命令的一部分的參數,將其與空格連接,然后傳遞至遠程shell(類似於"${SHELL:-sh}" -c "$*"
) 。 幸運的是, 如果您的遠程SHELL
是bash,bash具有內置的printf %q
操作(擴展名,因此在所有其他POSIX shell中不可用)可以使字符串為eval安全,從而為ssh安全。 有關使用Python的shlex
模塊在非bash shell中安全生成命令的變通方法, shlex
答案的結尾。
因此,如果您有在本地工作的命令,則第一步是將其放入數組(注意"$var_path"
擴展的引號,這對於進行明確的分組是必需的):
cmd=( sed -n '/]/{:a;n;/}/b;p;ba}' "$var_path" )
...您可以在本地運行以測試:
"${cmd[@]}"
...或轉換為eval-safe字符串:
printf -v cmd_str '%q ' "${cmd[@]}"
...然后使用ssh在本地運行...
ssh remote_host "$cmd_str"
...或使用eval
在本地進行測試:
eval "$cmd_str"
現在,您的特定用例有一些例外-需要將其用作文字命令將其引號或轉義,但如果您希望它們保留其特殊含義, 則不能將其引號。 &&
, |
和>
是示例。 幸運的是,您可以自己構建字符串的那些部分來解決此問題:
ssh remote_host "cat - >/tmp/test.tmp && $cmd_str >/tmp/new.conf.tmp"
...相當於本地數組擴展...
cat - >/tmp/test.tmp && "${cmd[@]}" >/tmp/new.conf.tmp
或當地評估
eval "cat - >/tmp/test.tmp && $cmd_str >/tmp/new.conf.tmp"
一個警告: printf %q
保證以bash
(或ksh,如果您在本地ksh中使用printf %q
話)來引用事物,將它們評估為與輸入完全匹配。 如果您的目標系統的外殼不支持$''
POSIX擴展,則此保證將不再可用,並且面對所有可能的輸入時,必須采用更有趣的技術來保證魯棒性。
幸運的是,Python標准庫包含一個函數,用於轉義任何符合POSIX的外殼程序的任意字符串:
quote_string() {
python -c '
import sys
try:
from pipes import quote # Python 2
except ImportError:
from shlex import quote # Python 3
print(" ".join([quote(x) for x in sys.argv[1:]]))
' "$@"
}
此后,當您需要一個等效於printf '%q ' "$@"
,即使遠程shell不是bash也可以使用的等效項時,可以運行:
cmd_str=$(quote_string "${cmd[@]}")
一旦使用雙引號,嵌入式命令就可以簡單地使用單引號。 之所以有效,是因為雙引號變量替換將嵌入式單引號視為常規字符...沒什么特別的。
var="Test text" var_path="/etc/file.txt"; echo "$var"|ssh root@$host "cat - > /tmp/test.tmp && sed -n '/]/{:a;n;/}/b;p;ba}' $var_path > /tmp/new.conf.tmp"
如果要在雙引號內分配和使用變量,則將更加棘手。
root @ anywhere很危險...沒有其他方法嗎? 如果您使用證書,希望它們將root限制為特定命令。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.