我突然意识到我不知道如何通过 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
.
安全 = 在参数中保留空格等。
理智 = 没有疯狂的引号转义。
在这个过程中首先要理解的是 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)。同样因为您使用的是标准输入,如果您希望远程端的脚本读取标准输入,您不能(至少不是没有一些技巧)。