我突然意识到我不知道如何通过 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,则您不能(至少需要一些技巧)。