bash中引号内的双引号

时间:2014-03-31 23:51:15

标签: bash quoting

我需要将$ 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按预期工作。

2 个答案:

答案 0 :(得分:2)

作为命令的一部分解析为在SSH中发送到远程系统的参数与空格连接,然后传递给远程shell(以类似于"${SHELL:-sh}" -c "$*"的方式)。幸运的是,bash具有内置的printf %q操作(扩展,因此在所有其他POSIX shell中不可用),以使字符串安全,因此ssh-safe, if 你的遥控器SHELL是bash;请参阅本答案的结尾,了解使用Python的shlex模块在​​非bash shell中生成命令安全的解决方法。

所以,如果你有一个在本地工作的命令,第一步是把它放到一个数组中(另请注意"$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"

附录:支持非Bash远程外壳

一个警告:printf %q保证以这样的方式引用内容:bash(或ksh,如果您在本地ksh中使用printf %q)将评估它们以完全匹配输入。如果你的目标系统有一个不支持POSIX扩展的shell,例如$'',那么这个保证将不再可用,并且需要更多有趣的技术来保证面对全方位的稳健性。可能的输入。

幸运的是,Python标准库包含一个函数来为任何符合POSIX的shell转义任意字符串:

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[@]}")

答案 1 :(得分:0)

使用双引号后,嵌入式命令可以简单地使用单引号。这是因为双引号变量替换将嵌入的单引号视为常规字符......没什么特别的。

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限制为特定命令。