《轉》Java Process應用之惑

加瓦攻城獅發表於2018-01-19

很多時候,我們需要呼叫系統命令來做些處理。比如,在程式中ping裝置是否能連線,執行資料庫的自動備份,以及程式的重啟。這時候我們必須要使用Process類來完成這些功能。 一般情況下,我們都會將命令執行過程中的資訊輸出,以便檢查問題。但有時候我們還需知道這個執行的程式在什麼時候結束,因為不僅要知道結束了,還要知道該程式完成時返回的結果。 可能會說,這些不都是API已經給提供好的嗎?過程中的訊息可以用process.getInputStream()獲取,程式最終結果可以由process.waitFor()得到。的確,這些看似可以辦到,但其實,裡面有陷阱。

首先以Runtime來建立我們需要的Process物件例子:

Java程式碼 收藏程式碼

Process process = null;  
        BufferedReader reader=null;  
        try {  
            process = Runtime.getRuntime().exec("ping 192.168.0.125");  
            reader=new BufferedReader(new InputStreamReader(process.getInputStream()));  
            String line=null;  
            while((line=reader.readLine())!=null){  
                System.out.println(line);  
            }  
            int result=process.waitFor();  
            System.out.println(result);  
        } catch (IOException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        } catch (InterruptedException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
複製程式碼

這個例子我們只是簡單的ping了一個IP,用getInputStream()輸出過程資訊,然後用process.waitFor()得到執行完成後的結果。會發現一點問題都沒有。 1.此時如果把process.getInputStream()換成process.getErrorStream(),就只有一個結果輸出了。 2.如果把執行的內容換成EXP導個oralce資料庫的命令呢!發現過程資訊和結果值都有,但是如果拿到一個server上去跑跑,又發現總是會報錯,rocess has not exited 程式未停止....在網上查詢,很多人會告訴你是輸出流導致程式阻塞,發生死鎖了。 3.此時你再把process.getErrorStream()改回process.getInputStream(),發現啥也沒有,而且程式不會停掉。這是肯定的,輸出流又導致程式阻塞了。 查API得知Process所有標準 io(即 stdin,stdout,stderr)操作都將通過三個流 (getOutputStream(),getInputStream(),getErrorStream()) 重定向到父程式。那為什麼執行系統自帶命令就能用getInputStream()獲取到資訊而不是系統自帶命令就一定要用getErrorStream()來獲取資訊呢!惑之...待高手解答... 獲取程式返回結果有兩個方法exitValue()和waitFor()。往往會發現流的堵塞無法使其得到值就報異常了。這個網上也有解答方法。即要清空getInputStream()和getErrorStream()這兩個流。而且兩個流的清空一定是非同步的。 Java程式碼 收藏程式碼

static void drainInBackground(final InputStream is) {  
            new Thread(new Runnable(){  
                public void run(){  
                    try{  
                        while( is.read() >= 0 );  
                    } catch(IOException e){   
                        // return on IOException                  
                    }  
                }  
            }).start();  
        }  



有沒有好的辦法不寫這個清空流的方法呢!或是還不明白,那就直接用ProcessBuilder來建立Process物件吧!ProcessBuilder已經給出了這方面的解決方案,但是必須要注意的是ProcessBuilder的redirectErrorStream方法。查API可知曉,redirectErrorStream方法設定為ture的時候,會將getInputStream(),getErrorStream()兩個流合併,自動會清空流,無需我們自己處理。如果是false,getInputStream(),getErrorStream()兩個流分開,就必須自己處理,程式如下:
複製程式碼

Java程式碼 收藏程式碼

try {  
            ProcessBuilder pbuilder=new ProcessBuilder("ping","192.168.0.125");  
            pbuilder.redirectErrorStream(true);  
            process=pbuilder.start();  
            reader=new BufferedReader(new InputStreamReader(process.getInputStream()));  
            String line=null;  
            while((line=reader.readLine())!=null){  
                System.out.println(line);  
            }     
            int result=process.waitFor();  
            System.out.println(result);  
        } catch (IOException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        } catch (InterruptedException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
複製程式碼

現在無論你呼叫的是系統自帶命令還是配置環境變數的其他命令,getInputStream()流都能給你過程資訊和執行結果。 如果redirectErrorStream設定為false,那結果會和上面所說一樣。

最後,還要說的是,得到的process.waitFor()結果。別以為這個執行結果值是一層不變的0,API沒有給出具體有多少種型別的返回值,就我測試的結果來看: 0 successfully 1 failure 3 successfully! but a warning ....2沒有測試出來

經過一個上午的不斷測試,找原始碼來看,查資料,終於將這個已經沒有認真理會的API缺陷透徹的梳理了一遍。 為了不重複別的話,很多基礎知識沒有描述,本著重點解決問題和存在的問題。

相關文章