【发布时间】:2011-10-09 20:34:18
【问题描述】:
我希望能够将 Java 应用程序中表示为字符串的许多连续命令发送到 SSH 服务器以供执行。我应该使用:
Channel channel = session.openChannel("shell");
-或-
Channel channel = session.openChannel("exec");
【问题讨论】:
我希望能够将 Java 应用程序中表示为字符串的许多连续命令发送到 SSH 服务器以供执行。我应该使用:
Channel channel = session.openChannel("shell");
-或-
Channel channel = session.openChannel("exec");
【问题讨论】:
您可以在 JSch wiki 中的 »Shell, Exec or Subsystem Channel« 中找到有关这些流之间差异和相似之处的概述。以下是您的用例的一些详细信息。
在exec channel 中,命令来自您使用setCommand() 提供的命令字符串。 SSH 服务器会立即将它们传递给 shell(使用类似 bash -c '<command>' 的东西)。
如果 shell 由于某种原因之前没有退出,它们都将被执行。 (如果需要,您可以在此处发送一个完整的 shell 脚本,该脚本使用 if 和类似方法实现一些逻辑。)
因此,要执行多个命令,您可以通过使用; 或换行符 (\n) 分隔它们将它们传递到 exec 通道。由于在给出所有命令之前您不能等待结果,因此您只能使用多个 exec 通道(但由于每个通道都会生成一个新的 shell,它们不会保存它们之间的状态,例如工作目录或 shell 变量)。
在shell channel 中,shell 将从流中读取输入,并将第一行解释为一个命令(或多个命令)。
然后它会执行这个命令。如果需要,命令本身可能会从流中读取更多输入。
然后shell会读取下一行,将其解释为命令,然后执行。
(在某些情况下,shell 必须读取多行,例如对于长字符串或组合命令,如 if 或循环。)
这将一直持续到流结束(例如您身边的 stream.close())或执行显式退出命令。
如果您不通过通道输入/输出流向外壳发送任何输入,外壳将简单地等待,直到您发送更多或关闭流。因此,您可以安静地读取一个命令的输出,在客户端进行一些计算,然后决定接下来发送哪个命令。
只要确保不要将一个命令的输入与下一个命令的文本混合在一起 - 最好不要使用将从标准输入读取的任何命令。
【讨论】:
使用 shell 通道启动 shell(在 unix 上是 sh 或 bash 或类似的东西,在 Windows 上通常是 cmd.exe)并创建控制台(如果您在本地运行它们,您会在屏幕上看到相同的内容)。您有一个提示,您可以解析或使用它来检测命令的完成。
使用命令通道会为每个命令启动一个 shell 实例(实际上是为每个命令打开通道),然后将一个命令作为 shell 的参数传递(在 Windows 上它看起来像“cmd.exe /c”。
使用命令通道更容易,因为您不需要处理命令提示符。
【讨论】:
; 或\n 将它们分开即可。
&在windows上完成(即与;在linux上的效果相同)。更多信息可以在这里找到(&&、||、())google.com.eg/…
好吧,我发现这很有效,如果您想像常规 shell 一样保留状态,这真的很方便:
Session session = jsch.getSession(user, host, 22);
Channel channel = session.openChannel("shell");
OutputStream inputstream_for_the_channel = channel.getOutputStream();
PrintStream commander = new PrintStream(inputstream_for_the_channel, true);
channel.setOutputStream(System.out, true);
channel.connect();
commander.println("ls -la");
commander.println("cd folder");
commander.println("ls -la");
commander.println("exit");
commander.close();
do {
Thread.sleep(1000);
} while(!channel.isEOF());
session.disconnect();
你可以改变
channel.setOutputStream(System.out, true);
与
InputStream outputstream_from_the_channel = channel.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(outputstream_from_the_channel));
String line;
while ((line = br.readLine()) != null)
System.out.print(line+"\n");
如果您想对输出进行更多控制。
================================================ ===============================
已编辑:跟进
为什么有时我通过 PrintStream 发送的命令会随机出现在输出中。 IE。 以下代码:
shell[0].println("cd ..");
shell[0].println("cd ..");
shell[0].println("ls -la");
shell[0].println("exit");
产生这个: (标有 {thing} 的是不应该存在的东西!)
上次登录时间:2011 年 7 月 21 日星期四 21:49:13 从网关
清单:trunk-latest
[主机 ~]$ cd ..
{cd ..}[主机主页]$
[主机主页]$ cd ..
[主机/]$
[主机 /]$ ls -la
{退出}总共 9999
---------- 27 根根 4096 2010 年 1 月 26 日。
---------- 27 根根 4096 2010 年 1 月 26 日 ..
---------- 1 根根 0 Mar 14 19:16 .autojyk
---------- 1 根 0 2009 年 2 月 9 日 .automan
---------- 1 根根 3550 2010 年 5 月 14 日 .bash_history
d--------- 2 root root 4096 Apr 26 04:02 put
d--------- 5 root root 4024 Apr 25 19:31 启动
[m[主机/]$
[主机 /]$ 退出
注销
【讨论】:
commander.println("echo 'COMMAND_BEGIN'"); commander.println("ls -la"); commander.println("echo 'COMMAND_END'");
这与 JSch 无关。是关于服务器如何实现这两个通道的。
通用 *nix OpenSSH 服务器:
shell 通道执行登录 shell(就像您使用 SSH 终端客户端登录一样)。然后,shell 将显示命令提示符并等待客户端/用户键入命令。 shell 通道的目的是实现交互式 shell 会话。这是一个人很少会做的事情。如果这样做,您通常希望使用终端仿真。
shell 通道显然是 SSH 终端客户端(如 OpenSSH ssh 或 PuTTY)在正常情况下使用的。
shell 通道是一个带有输入和输出的黑盒子。输入和输出没有结构。例如,如果您通过将命令发送到输入来执行命令,您将永远无法知道它何时结束。如果你向输入队列发送两个命令,你将无法区分什么输出来自什么命令。
exec 命令将命令作为“参数”并在隔离环境中执行它——仍然通过用户的默认 shell,但不是作为“登录”shell,这可能会导致命令执行存在显着差异。
exec 频道的目的是自动执行命令。所以通常你不想使用终端仿真,以避免命令做一些花哨的事情,比如分页、着色和主要是交互式确认。
exec 通道由 OpenSSH ssh 或 PuTTY plink 使用,当您指定要在其命令行上执行的命令时:
ssh user@host command
对于不太常见的 SSH 服务器,差异可能更加显着。有些服务器甚至可能不支持其中一个频道。它们似乎同时支持两者也很常见,但其中一个(通常是 exec)完全损坏了。
Python/Paramiko 也有类似的问题:
What is the difference between exec_command and send with invoke_shell() on Paramiko?
【讨论】: