通过 SSH 执行 `sh -c` 脚本(安全而理智地传递参数)

Kus*_*nda 6 ssh arguments

我突然意识到我不知道如何通过 SSH 执行事情。

我试着做

$ ssh user@server sh -c 'echo "hello"'
Run Code Online (Sandbox Code Playgroud)

但它什么都不输出,或者更确切地说,它输出一个空行。如果给出的命令在远程主机上ssh运行$SHELL -c,那么我可以理解为什么会这样(或者我可以尝试将其合理化)。

好的,第二次尝试:

$ ssh user@server 'echo "hello"'
hello
Run Code Online (Sandbox Code Playgroud)

一切都很好。

现在对于我真正希望的工作:

$ ssh user@server 'echo "hello $1"' sh "world"
hello  sh world
Run Code Online (Sandbox Code Playgroud)

嗯……sh从哪里来的?这表明这$1是空的,并且在另一侧真正执行的是类似的

$SHELL -c 'echo "hello  sh world"'
Run Code Online (Sandbox Code Playgroud)

而不是我所希望的,

$SHELL -c 'echo "hello $1"' sh "world"
Run Code Online (Sandbox Code Playgroud)

有没有办法安全地将参数传递给通过 执行的脚本ssh,以类似于运行的理智和明智的方式

sh -c 'script text' sh "my arg1" "my arg2" "my arg3" ...
Run Code Online (Sandbox Code Playgroud)

但在远程主机上?

我的本地和远程登录 shell 都是/bin/sh.


安全 = 在参数中保留空格等。

理智 = 没有疯狂的引号转义。

phe*_*mer 5

在这个过程中首先要理解的是 ssh 如何处理它的参数。我的意思不是你试图运行的东西的参数,而是 ssh 的参数。当您调用 时ssh,远程主机规范 ( user@server)之后的参数将连接在一起,并通过远程端的 shell。这一点很重要,因为仅仅因为您的参数在本地端正确拆分,并不意味着它们将在远程端正确拆分。

使用您的示例:

ssh user@server 'echo "hello $1"' sh "world"
Run Code Online (Sandbox Code Playgroud)

这些参数作为命令连接起来:

echo "hello $1" sh world
Run Code Online (Sandbox Code Playgroud)

这就是为什么你得到

hello  sh world
Run Code Online (Sandbox Code Playgroud)

hello和之间的双空格sh是因为那$1是应该去的地方,但没有$1.

 

再举一个例子,没有$1, 是:

ssh user@server echo "foo      bar" baz
Run Code Online (Sandbox Code Playgroud)

这导致输出:

foo bar baz
Run Code Online (Sandbox Code Playgroud)

这是因为参数被连接在一起,所以你最终得到命令:

echo foo      bar baz
Run Code Online (Sandbox Code Playgroud)

 

由于无法绕过通过 shell 传递的命令,因此您只需确保传递的命令可以在 shell 评估中幸存下来。我通常完成此操作的方式是printf "%q "

例如:

cmd=(echo "foo      bar" baz)
ssh user@server "$(printf "%q " "${cmd[@]}")"
Run Code Online (Sandbox Code Playgroud)

这导致输出:

foo      bar baz
Run Code Online (Sandbox Code Playgroud)

虽然cmd作为单独的 var更清晰、更容易理解,但这不是必需的。以下工作原理相同:

ssh user@server "$(printf "%q " echo "foo      bar" baz)"
Run Code Online (Sandbox Code Playgroud)

 

这也适用于您的 shell 参数示例:

cmd=(sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3")
ssh user@server "$(printf "%q " "${cmd[@]}")"
Run Code Online (Sandbox Code Playgroud)

这导致输出:

1=<my arg1> 2=<my arg2> 3=<my arg3>
Run Code Online (Sandbox Code Playgroud)

 


作为替代解决方案,您可以将命令作为完整的 shell 脚本传递。例如:

ssh user@server <<'EOF'
sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3"
EOF
Run Code Online (Sandbox Code Playgroud)

这个解决方案有一些缺点,因为它更难以编程方式进行(生成文档以传递 STDIN)。同样因为您使用的是标准输入,如果您希望远程端的脚本读取标准输入,您不能(至少不是没有一些技巧)。