2011-06-20 43 views
5

在命令行中一切正常,但是當我將所需內容翻譯成Java時,接收過程從不會在stdin上獲取任何內容。如何用Java寫入ssh的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. ************"); 
} 

腳本「計數的字節」很簡單:

#!/bin/bash 

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

輸出指示功能,在「廁所-c」線掛 - 它永遠不會進入「統計stdin字節」行。

發生了什麼事?會使用Jsch幫助嗎?

+0

當您使用IOCopy時,返回哪個值? – Snicolas

+0

我更改了代碼以使用copyLarge();它返回30054046. –

回答

4

您可能會在您期望wc -c返回之前嘗試關閉輸出流。

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

warFileInputStream.close(); 
deployWarFileStdout.close(); 
+0

我已經添加了從stdout讀取,以調試爲什麼原始腳本(ssh到另一臺機器)不起作用。在從標準輸出讀取stdin之前關閉stdin之後,該函數似乎可以工作(雖然它有時可以工作)。從標準輸出讀取是否強制腳本完成它正在執行的任何操作?那和調用waitFor()有什麼不同? –

+0

@Noel:這裏的問題是'copy'和'wc'的組合:'wc'只會在輸入關閉的時候開始輸出任何東西,'copy'會一直等待,直到有腳本輸出結束)。你可以把'copy'並且靠近一個單獨的線程。 –

0

你可能得到一個錯誤,但該過程將掛起,因爲你不讀error stream。從過程的JavaDoc

其所有標準IO兩者 (即標準輸入,標準輸出,標準錯誤)操作將通過三個流(Process.getOutputStream(),Process.getInputStream(),過程重定向到父進程。 getErrorStream())。父進程使用這些流將輸入提供給子進程並從子進程獲取輸出。由於某些本地平臺僅爲標準輸入和輸出流提供有限的緩衝區大小,因此無法及時寫入輸入流或讀取子流程的輸出流可能會導致子流程阻塞甚至死鎖。

所以你需要閱讀所有這些。使用ProcessBuilder可能更容易

+0

很好的建議,但在這種情況下,沒有錯誤產生。 –

1

會使用Jsch的幫助嗎?

使用JSch只會幫助,如果你將要使用的通道,而不是IOUtils.copy方法的setInputStream()setOutputStream()方法,因爲他們管理一個單獨的線程複製。

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(); 

(當然,如果你有較長的輸出,你會想要做的輸入和在第llel,或簡單地使用第一種方法。)