根据zanco's answer,您没有向ssh 提供远程命令,因为shell 是如何解析命令行的。要解决此问题,请更改 ssh 命令调用的语法,以便远程命令由语法正确的多行字符串组成。
可以使用多种语法。例如,由于命令可以通过管道传输到 bash 和 sh,可能还有其他 shell,最简单的解决方案是将 ssh shell 调用与 heredocs 结合起来:
ssh user@server /bin/bash <<'EOT'
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT
请注意,执行上述 /bin/bash 将导致警告Pseudo-terminal will not be allocated because stdin is not a terminal。还要注意EOT 被单引号包围,所以bash 将heredoc 识别为nowdoc,关闭局部变量插值以便命令文本将按原样传递给@ 987654332@.
如果你是管道爱好者,可以将上面的内容改写如下:
cat <<'EOT' | ssh user@server /bin/bash
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT
关于/bin/bash 的警告同样适用于上述内容。
另一种有效的方法是将多行远程命令作为单个字符串传递,使用多层bash 变量插值如下:
ssh user@server "$( cat <<'EOT'
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT
)"
上面的解决方案通过以下方式解决了这个问题:
ssh user@server 由 bash 解析,并被解释为 ssh 命令,后跟要传递给 ssh 命令的参数 user@server
" 开始一个内插字符串,完成后,将包含一个要传递给ssh 命令的参数,在这种情况下,ssh 将其解释为要执行的远程命令如user@server
$( 开始执行命令,输出被周围的插值字符串捕获
cat 是一个输出任何文件内容的命令。 cat 的输出将被传递回捕获的插值字符串
<< 开始一个 bash heredoc
'EOT' 指定heredoc 的名称是EOT。 EOT 周围的单引号' 指定应该将heredoc 解析为nowdoc,这是heredoc 的一种特殊形式,其中内容不会被bash 插值,而是以文字形式传递格式
<<'EOT' 和 <newline>EOT<newline> 之间遇到的任何内容都将附加到 nowdoc 输出中
EOT 终止 nowdoc,从而创建 nowdoc 临时文件并将其传递回调用 cat 命令。 cat 输出 nowdoc 并将输出传递回捕获的插值字符串
)结束要执行的命令
" 结束捕获插值字符串。内插字符串的内容将作为单个命令行参数传回ssh,ssh 将解释为远程命令以执行user@server
如果您需要避免使用cat 等外部工具,并且不介意使用两条语句而不是一条语句,请使用带有heredoc 的内置read 来生成SSH 命令:
IFS='' read -r -d '' SSH_COMMAND <<'EOT'
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT
ssh user@server "${SSH_COMMAND}"