2011-03-05 82 views
1

好的 - 這對很多人來說都有點問題 - 因爲我還沒有看到有效的答案,所以我想我會表達這個問題,這樣一個想出來的人可以告訴我們其他人。在Java中執行外部命令的經典問題?

問題是,下面三個工作中的兩個工作只是罰款代碼。

reader3的實例演示了這個問題。 Reader3無法讀取成功啓動外部文件的結果。試圖在任一標準輸入或標準錯誤的InputStream塊永遠做任何類型的讀取(realine等):

package Problems; 

import java.io.BufferedReader; 
import java.io.InputStreamReader; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 

public class RunningProblem { 

public static class RunningReader implements Runnable { 

    private Process proc; 
    private String sName; 

    private RunningReader(Process proc1, String sName) { 
     this.proc = proc1; 
     this.sName = sName; 
    } 

    public void run() { 
     try {     
      // InputStreamReader in = new InputStreamReader(proc.getInputStream()); 
      // BufferedReader reader = new BufferedReader(in); 

      InputStreamReader err = new InputStreamReader(proc.getErrorStream()); 
      BufferedReader reader = new BufferedReader(err); 

      String line = reader.readLine(); 
      while (line != null) { 
       System.out.println(sName + ": " + line); 
       line = reader.readLine(); 
      } 
      reader.close(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 

public static void main(String[] args) { 
    ExecutorService pool = Executors.newFixedThreadPool(3); 
    try { 
     Runtime rt = Runtime.getRuntime(); 

     Process proc1 = rt.exec("ps ax"); 
     RunningReader reader1 = new RunningReader(proc1, "reader1"); 

     Process proc2 = rt.exec("ls -l /"); 
     RunningReader reader2 = new RunningReader(proc2, "reader2"); 

     Process proc3 = rt.exec("/bin/tar"); 
     RunningReader reader3 = new RunningReader(proc3, "reader3"); 

     pool.execute(reader3); 
     pool.execute(reader2); 
     pool.execute(reader1); 

    } catch (Exception ex) { 
     System.err.println(ex.getMessage()); 
    } finally { 
     pool.shutdown(); 
    } 
    System.out.println("Launcher.main() Exited."); 
} 

}

+0

你確定你的/斌/焦油打印到標準錯誤(而不是標準輸出),並且你確定/斌/焦油,當它連接到一個管道確實停止? – nos 2011-03-05 18:39:00

+0

我正在研究的一個應用程序每天都在做這個,在數百個不同的機器上......我可以告訴你**一件**事情:有*方式*太多陷阱。我們最終確保不會讀取或寫入任何流。除此之外,我們從Java產生的過程本身也產生了另一個過程,所以我們可以從Java中終止第一個過程(過一段時間)。然後我們使用IPC的臨時文件。所以基本上我們在*「nohup ...&」*調用中包裝了任何外部進程調用(這也可以用來重定向stdout/stderr)。 – SyntaxT3rr0r 2011-03-05 19:03:06

+1

它有點糟糕,因爲我們需要輪詢臨時文件,但是它非常容易。只要閱讀試圖簡化* Runtime.getRuntim()。exec *)的各種API就必須說明從Java啓動外部進程:這是一個巨大的,混亂的,慘敗。如果你不想像我們那樣激進,那麼至少應該使用像Apache Commons exec *這樣的東西,並且閱讀他們對於從Java運行外部進程相當悲傷的狀態的看法。 :) – SyntaxT3rr0r 2011-03-05 19:06:04

回答

-1

你可能會開始RunningReaders晚。 您啓動了三個進程,在開始讀取RunningReader中的Error-OutputStream之前,可能有時會完成第一個進程。

RunningReaders必須在進程啓動後立即啓動。

在某些情況下甚至可能不足夠。然後,您將不得不創建一個包裝腳本(針對每個操作系統)來捕獲輸出並將其寫入文件。

+0

不,運行的閱讀器不必立即啓動。 – nos 2011-03-05 18:44:09

+0

我並不完全瞭解JDK大於1.4,但如果您沒有足夠快地捕獲輸出,它至少可以在Windows和Solaris上丟失 – vivo 2011-03-05 18:46:53

+0

這是鼠尾草 - 看起來不同的JVM在這個上的行爲不同。 ... – SaintMagoo 2011-03-06 12:44:04

1

你沒有顯示你的程序產出的輸出,但我想可能是這樣的,ps axls -l /不產出產量,但/bin/tar呢。原因是,前兩個命令產生的輸出爲stdout,但不是stderr,而後者將在stderr上產生輸出(因爲您沒有給出tar的有效參數),而是在stdout上產生輸出。在shell中運行命令時

這裏的區別:

[[email protected] tmp]$ ps ax > ps-std.txt 2> ps-err.txt 
[[email protected] tmp]$ ls -l/> ls-std.txt 2> ls-err.txt 
[[email protected] tmp]$ /bin/tar > tar-std.txt 2> tar-err.txt 
[[email protected] tmp]$ ls -lrt 
total 18 
-rw-r--r-- 1 axe users 0 Mar 5 19:40 ps-err.txt 
-rw-r--r-- 1 axe users 7191 Mar 5 19:40 ps-std.txt 
-rw-r--r-- 1 axe users 0 Mar 5 19:40 ls-err.txt 
-rw-r--r-- 1 axe users 937 Mar 5 19:40 ls-std.txt 
-rw-r--r-- 1 axe users 0 Mar 5 19:41 tar-std.txt 
-rw-r--r-- 1 axe users 142 Mar 5 19:41 tar-err.txt 
[[email protected] tmp]$ 

使用>重定向標準輸出和2>重定向錯誤輸出到不同的文件,你可以看到,tarstderr和所產生的消息另外兩個在stdout(其他文件的文件大小爲零,沒有輸出)。

可能是這種情況嗎?如果你運行,會發生什麼,例如。 G。 echo "Foo"而不是tar作爲第三個過程?

+0

大討論傢伙 - 謝謝! – SaintMagoo 2011-03-06 11:31:40

+0

我只是想從流程中得到一個響應 - 問題是任何流讀取嘗試都會永遠阻止 - reader3的任何輸出都會在上面,在我的平臺上取得成功 - (謝謝!) – SaintMagoo 2011-03-06 12:52:20

1

我跑我的系統上的代碼,它給了我下面的輸出,正常退出前:

 
reader3: /bin/tar: You must specify one of the `-Acdtrux' options 
reader3: Try `/bin/tar --help' or `/bin/tar --usage' for more information. 
Launcher.main() Exited. 

有沒有從ps ax也不ls -l /輸出,但是從殼體他們證實,他們沒」不要寫任何標準錯誤。在我的系統上,你的代碼正常完成,但我可以想象這種情況不會發生。請注意,如果某個進程在其標準輸出上生成大量輸出,則可能會填滿緩衝區,並導致進程掛起。

我推薦使用ProcessBuilder而不是 Runtime.getRuntime().exec("...")。首先,它允許您將標準錯誤流重定向到標準輸出流,然後不必擔心要讀取哪兩個流。

+0

是的,要麼重定向stderr/out,或者產生從它們讀取的線程 - 否則你很快就會使你產生的進程和你自己的java線程死鎖。 – nos 2011-03-05 21:25:32

+0

在Ubuntu上的Oracle JVM中,似乎存在這樣的因素:對於我來說,diff是'internal'-v-'external' - 每次選擇使用原始或ProcessBuilder方法時都會翻轉。 - OTD,WOT? – SaintMagoo 2011-03-06 12:48:38

0

Facinating(spockian眉) - 從上面的評論來看,它看起來有很好的理由,爲什麼如此多的人有這個問題 - 我們來實現各似乎完全不同的工作!

我正在運行Ubuntu。有趣的是,使用ProcessBuilder顛倒了這個問題......但是現在至少沒有一個人在使用它時似乎「永遠阻止」。 - 至少stderr和stdin能夠被讀取!對於我來說,現在的經驗法則似乎是:在Ubuntu(Oracle)上使用「舊方式」(Runtime.getRuntime().exec)作爲命令shell('內部')命令/孫VM) - 使用的ProcessBuilder外部命令(如焦油等):

Process proc3 = new ProcessBuilder("/bin/tar").start(); 
RunningReader reader3 = new RunningReader(proc3, "reader3"); 

...

reader3:/斌/焦油:您必須指定 的-Acdtrux' options reader3: Try的一個/ bin/tar --help'或`/ bin/tar --usage'以獲取更多信息。

- 對於我們很多人來說,這是一項非常重要的操作......將某個矩陣放在一起用於哪個平臺上? (即不知的OpenJDK會做得更好在Ubuntu?)