java Runtime.exec方法詳解!程式如何動態編譯輸入的程式碼並執行?!
在做一個專案時用到了Runtime.exec(String command),但是網上的很多部落格講解的都很不詳細明白,也不全,乾脆我自己來做一個講解。
我們先來看看api的解釋:
從字面上理解的意思就是在單獨開啟一條執行緒執行指定的命令。當然,這個方法有多個過載,如下:
下面會講到其它的過載方法,不急。
想到api的解釋我第一時間想到的就是cmd命令列!因為確實有些相似,但是轉念一想又不對,“在單獨的程序中執行指定的字串命令”不就是這玩意兒嗎!
然後我分別測試了這幾個命令“explorer”,“calc”,“notepad”。都能正確的開啟資源管理器、計算器和記事本程式。
但是cmd命令就不一樣了,cmd命令不會彈出視窗,可能是以上幾個命令是呼叫的外部的exe檔案,而cmd不是吧,具體機制我不清楚,呼叫cmd命令呢在網上我看到了如下幾種方法:
cmd /c dir 是執行完dir命令後關閉命令視窗。
cmd /k dir 是執行完dir命令後不關閉命令視窗。
cmd /c start dir 會開啟一個新視窗後執行dir指令,原視窗會關閉。
cmd /k start dir 會開啟一個新視窗後執行dir指令,原視窗不會關閉。
/c start 和 /k start 我沒測試過,主要使用的是/c,/k我測試了很多次,使用/k的話它怎麼都獲取不到輸出的資訊,無論是輸入流還是錯誤輸入流都不行!但是用/c就能正常獲取,所以我一直使用的是/c。
然後,重點來了!劃重點了!
誰說的呼叫一次exec方法不能執行多條cmd命令!
rt.exec("cmd /c set CLASSPATH=D:\\ && javac D:\\a.java && java a");
一個“&&”完美搞定~~
實測連線三條命令沒問題,上限沒測試過,各位看官無聊了可以去試試~~
但是,該方法有侷限性,它只能在cmd裡面使用,也就是前面必須有cmd三個字,你可以自己試試win+r呼叫cmd然後用&&連線多條命令執行,經過我的實測,是沒有問題的,也就是說exec方法本質上還是隻能呼叫一條命令的,我們能一次執行多條命令實際上是利用了cmd的語法的好處。
在上述程式碼中我先是設定了類路徑為D盤根目錄,完了編譯a.java檔案,緊接著執行a.class,在這裡如果不設定classpath屬性的話會導致執行a.class時報找不到類檔案的錯誤。
可能大家已經覺得了,在上面的程式碼中我要設定classpath的屬性又要javac一個完整的絕對路徑,有沒有什麼方法能夠一勞永逸呢?別說,還真有,這裡就會用到其它幾個過載的exec方法了。在這裡我們只講一個方法,因為其它的方法包括以上的那個方法最終都是呼叫它。
我們來一個引數一個引數的詳解,第一個引數cmdarray——包含所呼叫命令極其引數的陣列。
例如:shutdown -s -t 3600——在一小時後自動關機命令
我們可以構建這樣的一個數組:String arr[] = {"shutdown","-s","-t","3600"};
陣列第一個元素“shutdown”是命令,其後的三個-s、-t、3600都是引數,然後直接將這個陣列注入exec方法即可。
需要注意的是,在呼叫這個方法時我們不能將命令和引數放在一起——String arr[] = {"shutdown -s -t 3600"};
這樣去呼叫的話程式會把“shutdown -s -t 3600”當成是一條命令的名稱,它會去查詢“shutdown -s -t 3600”這條命令,它當然會找不到,所以就會報錯,想要偷懶的話我們可以呼叫這個方法,我自己平時也是呼叫的這個方法,比較方便:
這個過載的方法會自動將一條字串分解成命令和引數,最後再去呼叫我們現在講解的這個方法。
然後是第二個引數:envp——字串陣列,其中每個元素的環境變數的設定格式為 name=value,如果子程序應該繼承當前程序的環境,或該引數為null。
這個引數說白了就是去設定當前程序的環境變數,在上面的連線多條命令的例子中我們設定了classpath的路徑,classpath其實就是一個環境變數,所以我們在這裡就可以構建一個這樣的陣列:
String envp[] = {"CLASSPATH=D://"}
然後注入進exec方法即可,這樣的話就可以直接java a去執行a.class了,程式會到D盤的根目錄下去尋找a.class檔案。
但是在我的測試中發現了一個很怪異的現象就是如果你在呼叫該方法的時候提供了這個引數,並且命令是cmd開頭也就是在cmd中執行命令的時候,系統的環境變數就好像不存在了一樣!你的環境變數就只剩下你陣列提供的了。但是如果你不在cmd中執行命令,而是直接執行命令的話系統的環境變數又能發揮工作。
最後是第三個引數:dir——子程序的工作目錄;如果子程序應該繼承當前程序的工作目錄,則該引數為 null。
其實就是設定當前的目錄,一般要和cmd配合才能起作用,因為如果不在cmd裡面執行命令的話,設定了目錄也無濟於事,程式依舊只會去環境變數設定的目錄中尋找程式。
接下來我們就把上面的紅字的例子用這個方法呼叫:
String arr[] = {"CLASSPATH=D://","Path=C:\\Program Files\\Java\\jdk1.8.0_131\\bin"};
rt.exec("cmd /c javac a.java && java a", arr, new File("D://"));
在上述程式碼中我設定的環境變數陣列使得命令能找到a.class和java這兩個檔案,因為第二個引數的怪異性,所以我這裡還要設定java檔案路徑才能正常工作,最後設定的目錄使得命令能找到a.java檔案。
最後,大家應該注意到了,exec方法還有一個返回的物件Process,這個物件是給我們用來控制exec方法建立的程序的,該物件可用來控制程序並獲得相關資訊。Process 類提供了執行從程序輸入、執行輸出到程序、等待程序完成、檢查程序的退出狀態以及銷燬(殺掉)程序的方法。 我將在下篇文章詳細講解Process類,我們下次再見~~拜拜~~