30

我希望能够在 Java 应用程序中将许多以字符串表示的连续命令发送到 SSH 服务器以供执行。我应该使用:

Channel channel = session.openChannel("shell");

-或者-

Channel channel = session.openChannel("exec");
4

4 回答 4

19

您可以在 JSch wiki 中的» Shell、Exec 或 Subsystem Channel « 中找到有关这些流之间差异和相似之处的概述。这是您的用例的一些详细信息。

exec频道中,命令来自您提供的命令字符串setCommand()。SSH 服务器会立即将它们传递给 shell(使用类似的东西bash -c '<command>')。

如果外壳由于某种原因之前没有以某种方式退出,它们都将被执行。(如果需要,您可以在此处发送一个完整的 shell 脚本,该脚本使用if和类似的方法实现一些逻辑。)

因此,要执行多个命令,您可以通过将它们用;或换行符 ( \n) 分隔来将它们传递到 exec 通道。由于在给出所有命令之前您不能等待结果,因此您只能使用多个 exec 通道(但由于每个通道都会产生一个新的 shell,它们不会保存它们之间的状态,例如工作目录或 shell 变量)。

shellchannel中,shell 将从流中读取输入,并将第一行解释为一个命令(或多个命令)。

然后它将执行此命令。如果需要,命令本身可能会从流中读取更多输入。

然后 shell 将读取下一行,将其解释为命令并执行。

(在某些情况下,shell 必须读取多于一行,例如对于长字符串或组合命令,如 if 或循环。)

这将一直持续到流结束(例如您身边的 stream.close() )或执行显式退出命令。

如果您不通过通道输入/输出流向外壳发送任何输入,外壳将简单地等待,直到您发送更多或关闭流。因此,您可以安静地读取一个命令的输出,在客户端进行一些计算,然后决定接下来发送哪个命令。

只要确保不要将一个命令的输入与下一个命令的文本混合在一起——最好不要使用任何从标准输入读取的命令。

于 2011-07-21T01:04:52.663 回答
18

使用 shell 通道启动 shell(在 unix 上是 sh 或 bash 或类似的东西,在 Windows 上通常是 cmd.exe)并创建控制台(如果您在本地运行它们,您会在屏幕上看到相同的内容)。您有一个提示,您可以解析或使用它来检测命令的完成。

使用命令通道,会为每个命令启动一个 shell 实例(实际上是为每个命令打开通道),并将一个命令作为 shell 的参数传递(在 Windows 上,它看起来像“cmd.exe /c”。

使用命令通道更容易,因为您不需要处理命令提示符。

于 2011-07-21T04:57:14.400 回答
8

好吧,我发现这很有效,如果你想像普通 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 发送的命令会随机出现在输出中。即以下代码:

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

[host ~]$ cd ..
{cd ..}[host home]$
[host home]$ cd ..
[host /]$
[host /]$ ls -la
{exit}

总计 9999
---------- 27 根 4096 2010 年 1 月 26 日。
---------- 27 root root 4096 Jan 26 2010 ..
---------- 1 root root 0 Mar 14 19:16 .autojyk
---------- 1 root root 0 Feb 9 2009 .automan
---------- 1 root root 3550 May 14 2010 .bash_history
d--------- 2 root root 4096 Apr 26 04:02 put
d- -------- 5 root root 4024 Apr 25 19:31 boot
[m[host /]$
[host /]$ exit
logout

于 2011-07-21T17:18:54.000 回答
5

这与 JSch 无关。它是关于服务器如何实现这两个通道的。


常见的 *nix OpenSSH 服务器:

  • shell通道执行一个登录 shell(就像您使用 SSH 终端客户端登录一样)。然后,shell 将显示命令提示符并等待客户端/用户键入命令。通道的目的shell是实现交互式 shell 会话。这是一个人很少会做的事情。如果这样做,您通常希望使用终端仿真。

    在正常情况下,该shell通道显然由 SSH 终端客户端(如 OpenSSHssh或 PuTTY)使用。

    shell通道是一个带有输入和输出的黑盒子。输入和输出没有结构。例如,如果您通过将命令发送到输入来执行命令,您将永远无法知道它何时结束。如果您向输入队列发送两个命令,您将无法区分什么输出来自什么命令。

  • exec命令将命令作为“参数”并在隔离环境中执行它——仍然通过用户的默认 shell,但不是作为“登录”shell,这可能会导致命令执行的显着差异。

    通道的目的exec是自动执行命令。所以通常你不想使用终端仿真,以避免命令做一些花哨的事情,比如分页、着色和主要的交互式确认。

    exec当您指定要在其命令行上执行的命令时,OpenSSHssh或 PuTTY使用通道:plink

    ssh user@host command
    

对于不太常见的 SSH 服务器,差异可能会更加显着。有些服务器甚至可能不支持其中一个频道。它们似乎同时支持两者也很常见,但其中一个(通常是exec)完全损坏了。


Python/Paramiko 有一个类似的问题:
exec_command 和在 Paramiko 上使用 invoke_shell() 发送有什么区别?

于 2019-06-22T07:32:43.117 回答