我想要一种简单的方法,从本地 Windows 计算机在远程 Linux 计算机上运行命令。为此,我将使用通过 SSH 发送命令的批处理脚本。我在这两种方式上都遇到了麻烦
直接给出
ssh
bash 命令并或者指向
ssh
要.sh
发送的 shell 脚本。
解决方案 1:
ssh -T [email protected] 'echo hello, world'
给出bash: echo hello, world: command not found
该命令似乎被解析为一个标记,而不是将其echo
视为命令并将其余部分视为参数。
解决方案 2:
:: script.bat
@echo off
ssh -T [email protected] < script.sh
pause
。
# script.sh
echo pt1
echo pt2
if [ -n "str" ]; then
echo pt3
fi
echo pt4
这次尝试似乎通过 SSH 完成前 2 个命令,然后无限期地挂在语句上。即使它能做到那么远,if
它也不会超越条件。if
解决方案摘要:
Kamil 的解释非常深入,包括echo
工作原理。读起来很有帮助。
我已经对CRLF
s 产生了怀疑。收到反馈后,在余下的调试过程中,我使用了;
-delimited 单行代码。
@xenoid 无意中指出,@Kamil 也注意到我的引用在1是错误的。我使用的是单引号,但这是严格引用,其中除 之外的所有内容'
都是文字。不使用引号或使用双引号可提供预期的行为。
我还是无法得到2工作。@Kamil 有一个很好的解决方案,只需将 shell 脚本放在 Linux 机器上即可。我会用这个。
答案1
部分答案。我无法解释脚本的行为,但我可以解释这一点:
ssh -T [email protected] 'echo hello, world'
给出bash: echo hello, world: command not found
该命令似乎被解析为一个标记,而不是将其
echo
视为命令并将其余部分视为参数。
解释如下这个答案上面写着:
命令处理器根本不使用单引号,
cmd.exe
除非将要运行的命令括在FOR /F
语句中
这意味着您ssh
获得了以下参数:-T
,,,。[email protected]
单引号实际上已经到达了工具。'echo hello,
world'
的参数ssh
(不被识别为选项、选项参数或[user@]hostname
)将被视为命令或命令的一部分。后面的参数(如果有)也被视为命令的一部分。如果有两个或多个部分,则将ssh
通过连接它们来为远程 shell 构建命令,并在它们之间插入单个空格。
在您的情况下,这两个部分('echo hello,
,world'
)给出了此代码在远程 shell 中运行:
'echo hello, world'
简单命令的解决方案:使用双引号,它们将被解释cmd.exe
。此代码:
ssh -T [email protected] "echo hello, world"
将传递以下参数给ssh
:-T
、[email protected]
、echo hello, world
。这正是您最初想要的。远程 shell 将获得:
echo hello, world
请注意,当你说“将echo
其视为命令,其余部分视为参数”时,情况并非如此。其余部分是参数s,复数。在远程端echo
将获得二参数:hello,
和world
。它会将它们打印为,hello, world
因为在这种情况下,工具会注入一个空格。如果您需要在两者之间添加多个空格,那么您应该这样做:
ssh -T [email protected] "echo 'hello, world'"
这里双引号不会cmd.exe
分割字符串,因此ssh
使用这些空格获取它。然后在远程端,单引号使远程 shell 不会分割(较短的)字符串,因此echo
使用这些空格获取它。省略任何一对引号都会导致hello, world
。哪种机制负责用一个空格替换多个空格取决于您省略了哪些引号。
更复杂的命令通常需要修改(特别是如果它们包含双引号),以防止本地解释器干扰。链接的答案指出:
"
以下是有关使用双引号的一些重要注意事项cmd.exe
。
双引号是一个简单的状态机。第一个
"
状态机打开引号,第二个状态机关闭引号。所有特殊字符在引用时都被视为文字值,除了
%
和<newLine>
字符始终,以及!
如果启用了延迟扩展则为的字符,以及^
如果启用了延迟扩展并且!
也出现在行内的某处。如果引用已关闭,那么您可以转义
"
as^"
以防止其打开引用。但是一旦打开引用,您就无法转义结尾的"
。下一个"
将始终关闭引用。
这种不便也发生在(本地)Bash 或 Linux 中的任何其他 shell 中。引用规则不同,但您仍然需要了解语法的哪些部分将在本地端解释,哪些将在远程端解释。在 Bash 中,您可以使用单引号或 here documents,这通常会有所帮助。
防止本地解释器更改远程解释器代码的一个可靠方法是使代码在本地端不显示为代码。这就是您通过从文件读取代码所尝试执行的操作。我不了解 Windows,也cmd.exe
不太了解该方法为何不起作用;我只知道它在 Linux 和 Linux 之间都有效,所以最有可能的罪魁祸首不是独自在你的远程 Linux 中。
由于代码在文件中,另一种方法是将文件本身复制到远程端。然后,您可以在本地调用如下命令cmd.exe
:
ssh [email protected] ./script.sh
这需要脚本在远程端可执行。还建议在脚本中使用适当的 shebang。您可以通过以下方式规避这些限制:
ssh [email protected] "/bin/sh ./script.sh"
(或/bin/bash
脚本设计的任何内容)。请注意,此处的双引号并非绝对必要。我只是倾向于引用使用 调用的整个多字命令ssh
。
scp
可以通过、FTP、Samba 或其他方法复制脚本。不过有一些怪癖:
Linux/Unix 中的 Shell 要求脚本使用 Unix 行尾:LF。Windows 中的文本文件使用 CR+LF。大多数协议都会按原样复制文件,无需转换。FTP 是个例外(如果您知道它的工作原理,则很有用,如果您不知道,则容易出错;比较我的这个答案)。可能的方法:
- 在 Windows 中使用文本编辑器,可以保存带有 Unix 行尾的文件。无需转换即可将文件复制到 Linux。
- 在 Windows 中使用 Windows 行尾。以文本模式通过 FTP 将文件复制到 Linux。
- 以任何方式创建,以任何方式复制。终于在 Linux 中使用
dos2unix
。如果文件已经是 Unix 格式,则dos2unix
只会用其精确副本替换文件;这几乎就像一个无操作。很少 缺点一般情况下是这样,但就你的情况而言,这应该不成问题。所以你可以dos2unix
放心使用。
POSIX要求每行(包括最后一行)后面都有一个换行符。如果脚本(通常是文本文件)的最后一行不是以 LF 结尾的,某些工具可能会忽略它,就像该行不存在一样。Linux 中的常见 shell 似乎接受不完整的行,但其他工具可能不接受。