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

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

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

我尝试做

$ ssh user@server sh -c 'echo "hello"'

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

好的,第二次尝试:

$ ssh user@server 'echo "hello"'
hello

一切都很好。

现在我真正希望的事情是:

$ ssh user@server 'echo "hello $1"' sh "world"
hello  sh world

嗯...从哪里来的sh?这表明它$1是空的,而另一边真正执行的内容是这样的

$SHELL -c 'echo "hello  sh world"'

而不是我所希望的,

$SHELL -c 'echo "hello $1"' sh "world"

ssh有没有一种方法可以以类似于运行的理智且合理的方式安全地将参数传递给通过执行的脚本

sh -c 'script text' sh "my arg1" "my arg2" "my arg3" ...

但在远程主机上?

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


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

Sanely = 没有疯狂地转义引号。

答案1

在此过程中首先要了解的是 ssh 如何处理其参数。我的意思不是你要运行的东西的参数,而是 ssh 的参数。当您调用 时ssh,远程主机规范 ( ) 后面的参数user@server将连接在一起,并通过远程端的 shell 传递。值得注意的是,因为仅仅因为您的参数在本地端正确分割,并不意味着它们将在远程端正确分割。

使用你的例子:

ssh user@server 'echo "hello $1"' sh "world"

这些参数连接为命令:

echo "hello $1" sh world

这就是为什么你得到

hello  sh world

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

 

另一个例子,没有$1, 的是:

ssh user@server echo "foo      bar" baz

其结果是输出:

foo bar baz

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

echo foo      bar baz

 

由于无法绕过通过 shell 传递的命令,因此您只需确保传递的内容能够在 shell 评估中幸存下来。我通常完成此任务的方法是printf "%q "

例如:

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

其结果是输出:

foo      bar baz

虽然作为一个单独的变量更干净、更容易理解cmd,但这不是必需的。下面的工作原理是一样的:

ssh user@server "$(printf "%q " echo "foo      bar" baz)"

 

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

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

其结果是输出:

1=<my arg1> 2=<my arg2> 3=<my arg3>

 


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

ssh user@server <<'EOF'
sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3"
EOF

但此解决方案也有缺点,因为它更难以编程方式执行(生成文档以传递 STDIN)。另外,因为您使用的是 STDIN,所以如果您希望远程端的脚本读取 STDIN,则您不能(至少需要一些技巧)。

相关内容