如何用Java写入ssh的stdin?

时间:2011-06-20 17:02:36

标签: java ssh pipe stdin

在命令行上一切正常,但是当我将我想要的内容翻译成Java时,接收过程永远不会在stdin上得到任何东西。

这就是我所拥有的:

private void deployWarFile(File warFile, String instanceId) throws IOException, InterruptedException {
    Runtime runtime = Runtime.getRuntime();
    // FIXME(nyap): Use Jsch.
    Process deployWarFile = runtime.exec(new String[]{
            "ssh",
            "gateway",
            "/path/to/count-the-bytes"});

    OutputStream deployWarFileStdin = deployWarFile.getOutputStream();
    InputStream deployWarFileStdout = new BufferedInputStream(deployWarFile.getInputStream());
    InputStream warFileInputStream = new FileInputStream(warFile);

    IOUtils.copy(warFileInputStream, deployWarFileStdin);
    IOUtils.copy(deployWarFileStdout, System.out);

    warFileInputStream.close();
    deployWarFileStdout.close();
    deployWarFileStdin.close();

    int status = deployWarFile.waitFor();
    System.out.println("************ Deployed with status " + status + " file handles. ************");
}

脚本'count-the-bytes'只是:

#!/bin/bash

echo "************ counting stdin bytes ************"
wc -c
echo "************ counted stdin bytes ************"

输出表明该函数在'wc -c'行挂起 - 它永远不会到达'count stdin bytes'行。

发生了什么事?会用Jsch帮忙吗?

3 个答案:

答案 0 :(得分:4)

您可能会在期望wc -c返回之前尝试关闭输出流。

IOUtils.copy(warFileInputStream, deployWarFileStdin);
deployWarFileStdin.close();
IOUtils.copy(deployWarFileStdout, System.out);

warFileInputStream.close();
deployWarFileStdout.close();

答案 1 :(得分:1)

  

会使用Jsch帮助吗?

使用JSch只会在您使用频道的setInputStream()setOutputStream()方法而不是IOUtils.copy方法时提供帮助,因为它们在单独的线程上管理复制。 / p>

ChannelExec deployWarFile = (ChannelExec)session.openChannel("exec");

deployWarFile.setCommand("/path/to/count-the-bytes");

deployWarFile.setOutputStream(System.out);
deployWarFile.setInputStream(new BufferedInputStream(new FileInputStream(warFile)));

deployWarFile.connect();

(这里你不得不等到对方关闭频道。)

如果您只是在打开ChannelExec时更换Runtime.exec(并在获取流后启动它),问题将完全相同,并且可以通过antlersoft提到的相同解决方案解决,即关闭在读取输出之前输入:

ChannelExec deployWarFile = (ChannelExec)session.openChannel("exec");

deployWarFile.setCommand("/path/to/count-the-bytes");

OutputStream deployWarFileStdin = deployWarFile.getOutputStream();
InputStream deployWarFileStdout = new BufferedInputStream(deployWarFile.getInputStream());
InputStream warFileInputStream = new FileInputStream(warFile);

deployWarFile.connect();

IOUtils.copy(warFileInputStream, deployWarFileStdin);
deployWarFileStdin.close();
warFileInputStream.close();

IOUtils.copy(deployWarFileStdout, System.out);
deployWarFileStdout.close();

(当然,如果你有更长的输出,你会想要并行输入和输出,或者只是使用第一种方法。)

答案 2 :(得分:0)

您可能会收到错误,但由于您没有阅读error stream,因此该过程会挂起。 取自Process JavaDoc

  

所有标准的io(即stdin,stdout,stderr)操作都将通过三个流(Process.getOutputStream(),Process.getInputStream(),Process.getErrorStream())重定向到父进程。父进程使用这些流向子进程提供输入并从子进程获取输出。由于某些本机平台仅为标准输入和输出流提供有限的缓冲区大小,因此无法及时写入输入流或读取子进程的输出流可能导致子进程阻塞,甚至死锁。

所以你需要阅读所有这些内容。使用ProcessBuilder可能更容易