如何在 ssh 和 sudo 的使用中转义 shell 中的引号?

如何在 ssh 和 sudo 的使用中转义 shell 中的引号?

总而言之:

问题和示例可以在本地测试:

sh -c "echo 'how to print single quote here'"

细节:

我有一个这样的配置:

upload_server = ('192.168.1.1', 10051)

现在我需要一个 shell 脚本来替换服务器配置。在本地我可以使用 sed 来完成:

sed -E 's/(    upload_server = ).*/\\1('\"'$a'\"', 10051)/g' config.py

但有大量的机器必须更改配置。

正确的计划是ssh与 for..in 一起使用:

for i in `cat hosts`; do ssh $i "cmd"; done

但要更改配置还需要一个sudo,它看起来像这样:

for i in `cat hosts`; do ssh $i "sudo -c \"cmd\""; done

现在在 sed 中:

upload_server=123.123.123.123
upload_server_port=1000
cmd="sed -E 's/(    upload_server = ).*/\\1('\"\"'\"'${upload_server}'\"'\"\"', ${upload_server_port})/g' config.py"
ssh $i "sudo -c \"${cmd}\""

配置结果:

upload_server = (123.123.123.123, 1000)

应该是这样的:

upload_server = ('123.123.123.123', 1000)

现在我对引号的用法感到很困惑。:(

答案1

在这种情况下,您可以通过在标准输入上传递 sed 脚本来完全绕过引用问题。 SSH 将本地标准输入路由到远程进程,sudo 只是将其保持打开状态,而 sed 可以在 stdin 上读取其脚本。您可以使用以下命令传递标准输入的内容这里的文档

for i in `cat hosts`; do
  ssh "$i" 'sudo sed -f - …' <<'EOF'
s/(    upload_server = ).*/\1("$a", 10051)/g
EOF
done

更一般地说,只要您不需要在 stdin 上传递数据,您就可以通过 SSH 在 stdin 上传递 shell 片段,而不必担心引用。

for i in `cat hosts`; do
  ssh "$i" sudo sh <<'EOF'
sed 's/(    upload_server = ).*/\1("$a", 10051)/g' …
EOF
done

从您的问题中不清楚您是否打算插入"$a"配置文件,或者本地 shell 中"something"变量的值在哪里。如果你想引用局部变量,那么使用带有插值的here文档,并在here文档中用反斜杠进行保护。请注意,由于由 sed 和第二个片段中的远程 shell 进行解析,变量的值不得包含任何字符或换行符。asomething$\`\/'

  ssh "$i" sudo sh <<EOF
sed 's/(    upload_server = ).*/\\1("$a", 10051)/g' …
EOF

答案2

$ ssh localhost sh -c \"echo \\\'how to print single quotes here\\\'\"
'how to print single quotes here'

$ ssh localhost echo \\\'how to print single quotes here\\\'
'how to print single quotes here'

要理解这些规则,请记住本地 shell 会删除一级引用,然后 SSH 会调用远程 shell,该远程 shell 会删除第二级引用。

相关内容