Java魔法堂之調(diào)用外部程序的方法
前言
Java雖然五臟俱全但總有軟肋,譬如獲取CPU等硬件信息,當(dāng)然我們可以通過JNI調(diào)用C/C++來獲取,但對于對C/C++和Windows API不熟的碼農(nóng)是一系列復(fù)雜的學(xué)習(xí)和踩坑過程。那能不能通過簡單一些、學(xué)習(xí)成本低一些的方式呢?答案是肯定的,在功能實現(xiàn)放在首位的情況下,借他山之石是最簡潔有力的做法。
認(rèn)識java.lang.Runtime#exec
方法
作用:用于調(diào)用外部程序,并重定向外部程序的標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤到緩沖池。功能就是和windows的“運行”一樣。
重載方法說明
Runtime#exec(String command); Runtime#exec(String command, String[] envp); Runtime#exec(String command, String[] envp, File workdir); Runtime#exec(String[] cmdArray); Runtime#exec(String[] cmdArray, String[] envp); Runtime#exec(String[] cmdArray, String[] envp, File workdir);
String[] envp
作為調(diào)用命令前設(shè)置的會話級環(huán)境變量。
1.1. 變量作用域:命令運行結(jié)束后,通過該參數(shù)設(shè)置的環(huán)境變量將失效;
1.2. 設(shè)置方式:variableName=variableValue
,如Process proc = r.exec("cmd /c dir > %dest%", new String[]{"dest=c:\\dir.txt"});
File workdir
用于設(shè)置當(dāng)前工作目錄,譬如我們需要執(zhí)行位于D:\tools下的echo.exe程序,那么可以這樣調(diào)用Process proc = r.exec("echo.exec", null, new File("D:\\tools"));
String command
即為需要調(diào)用的外部程序,以及命令行參數(shù)等。Windows下調(diào)用系統(tǒng)命令,像dir等命令是由cmd解析器解釋執(zhí)行的,因此若直接寫"dir"則會被認(rèn)為在當(dāng)前工作目錄下有一個"dir.exe"文件,那么當(dāng)然會執(zhí)行失?。辉贚inux下調(diào)用ls等是同樣道理,因此請按如下方式調(diào)用cmd和shell命令:
3.1. 調(diào)用CMD命令的方式為Process proc = r.exec(String.format("cmd /c %s", "cmd命令,如dir、type等"))
,若要啟動一個新的Console執(zhí)行命令,只需要將dir
改寫為start dir
即可;
3.2. 調(diào)用Shell命令的方式為Process proc = r.exec(String.format("/bin/sh -c %s", "shell命令,如ls、cat等"))
,若要啟動一個新的Terminal執(zhí)行命令,只需要將ls
改寫為xterm -e ls
即可;String[] cmdArray
功能和String command
一樣,但命令行的每個部分將作被獨立分隔出來作為數(shù)組中的元素。如cmd /c dir
必須分割為new String[]{"cmd", "/c", "dir"}
,而不能分割為new String[]{"cmd /c", "dir"}
。
輸入重定向
廢話少說,直接看代碼!
try { String cmd = "cmd /c start cmd.exe"; Process child = Runtime.getRuntime().exec(cmd); OutputStream output = child.getOutputStream(); output.write("cd C:/ /r/n".getBytes()); output.flush(); output.write("dir /r/n".getBytes()); output.close(); } catch (IOException e) {}
輸出重定向
注意:不支持直接使用>或>>執(zhí)行標(biāo)準(zhǔn)輸出重定向。
String cmd = "/path/to/getipconfig.bat"; // 自己寫的bat腳本文件,里面包含ipconfig /all命令。 Process p; try { p = Runtime.getRunTime().exec(cmd); InputStream input = p.getInputStream(); InputStreamReader streamReader = new InputStreamReader(input); BufferedReader bufReader = new BufferedReader(streamReader); String line = null; while ((line = bufReader.readLine()) != null) { System.out.println(line); } } catch (IOException e) {}
為什么不能使用>
和>>
實現(xiàn)輸出重定向呢?
通過Process實例.getInputStream()
和Process實例.getErrorStream()
獲取的輸入流和錯誤信息流是緩沖池是當(dāng)前Java程序提供的,而不是直接獲取外部程序的標(biāo)準(zhǔn)輸出流和標(biāo)準(zhǔn)錯誤流。
即通過Runtime#exec
調(diào)用外部程序時,外部程序的標(biāo)準(zhǔn)輸出流和標(biāo)準(zhǔn)錯誤流已經(jīng)被Java程序接管。那么在命令中企圖通過>
和>>
實現(xiàn)輸出重定向顯然已無效果。
另外,緩沖池的容量是一定的,因此若外部程序在運行過程中不斷向緩沖池輸出內(nèi)容,當(dāng)緩沖池填滿,那么外部程序?qū)和_\行直到緩沖池有空位可接收外部程序的輸出內(nèi)容為止。(采用xcopy命令復(fù)制大量文件時將會出現(xiàn)該問題)
解決辦法就是當(dāng)前的Java程序不斷讀取緩沖池的內(nèi)容,從而為騰出緩沖池的空間。如:
Runtime r = Runtime.getRuntime(); try{ Process proc = r.exec("cmd /c dir"); // 假設(shè)該操作為造成大量內(nèi)容輸出 // 采用字符流讀取緩沖池內(nèi)容,騰出空間 BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream(), "gbk"))); String line = null; while ((line = reader.readLine()) != null){ System.out.println(line); } // 或采用字節(jié)流讀取緩沖池內(nèi)容,騰出空間 // ByteArrayOutputStream pool = new ByteArrayOutputStream(); // byte[] buffer = new byte[1024]; // int count = -1; // while ((count = proc.getInputStream().read(buffer)) != -1){ // pool.write(buffer, 0, count); // buffer = new byte[1024]; // } // System.out.println(pool.toString("gbk")); int exitVal = proc.waitFor(); System.out.println(exitVal == 0 ? "成功" : "失敗"); } catch(Exception e){ e.printStackTrace(); }
注意:外部程序在執(zhí)行結(jié)束后將會自動關(guān)閉,否則不管是字符流還是字節(jié)流均由于既讀不到數(shù)據(jù),又讀不到流結(jié)束符而出現(xiàn)阻塞Java進程運行的情況。
簡化輸入輸出重定向的java.lang.ProcessBuilder
功能和java.lang.runtime#exec一樣
,只是java.lang.ProcessBuilder
僅接收命令行以數(shù)組形式傳遞給java.lang.ProcessBuilder#command()
而已。
基本使用
ProcessBuilder pb = new ProcessBuilder(); // pb.command("cmd /c dir"); 這樣會報錯 // 或者寫到new ProcessBuilder("cmd", "/c", "dir") pb.command("cmd", "/c", "dir"); try { Process process = pb.start(); // 后面都是操作Process實例的方法 } catch (IOException e){}
重定向
public ProcessBuilder redirectInput(File file) // 輸入重定向 public ProcessBuilder redirectOutput(File file) // 輸出重定向 public ProcessBuilder redirectError(File file) // 異常重定向
示例
try { ProcessBuilder pb = new ProcessBuilder("cmd"); // 在標(biāo)準(zhǔn)輸入中,通過換行符分隔多行命令。 // commands.txt的內(nèi)容為 // javac Demo.java // java Demo File commands = new File("/path/to/commands.txt"); File error = new File("/path/to/error"); File output = new File("/path/to/output"); pd.redirectInput(commands); pd.redirectOutput(output); pd.redirectError(error); pd.start(); } catch(Exception e) { e.printStackTrace(); }
java.lang.Process
API說明
// 以非阻塞方式獲取子進程執(zhí)行的返回值(習(xí)慣0表示正常結(jié)束)。若子進程尚未完成時調(diào)用該方法,則會報異常`java.lang.IllegalThreadStateException` int exitValue() // 以阻塞方式獲取子進程執(zhí)行的返回值。若進程尚未完成則會等待子進程完成后才恢復(fù)當(dāng)前線程。 // 問題:若子進程無法正常關(guān)閉,則會導(dǎo)致Java線程一直掛起; // 返回值為子進程的退出碼 int waitFor()。 // 如果超時前子進程結(jié)束,那么返回`true` ,否則返回`false` boolean waitFor(long timeout, TimeUnit unit) // 強行終止子進程,但調(diào)用后子進程不會馬上被終止,所以立即調(diào)`boolean isAlive()`方法可能會返回`true`,因此需配合`waitFor`使用。 void destory() // 默認(rèn)實現(xiàn)為調(diào)用`void destory()`方法。從JDK1.8開始提供。 Process destoryForcibly() // 如果子進程還沒結(jié)束則返回`true` 。從JDK1.8開始提供。 boolean isAlive() // 獲取子進程的異常輸出流,如果子進程以`ProcessBuilder`創(chuàng)建,且通過`ProcessBuilder.redirectError`設(shè)置重定向,那么該方法返回`null` InputStream getErrorStream() // 獲取子進程的標(biāo)準(zhǔn)輸出流,如果子進程以`ProcessBuilder`創(chuàng)建,且通過`ProcessBuilder.redirectOutput`設(shè)置重定向,那么該方法返回`null` InputStream getInputStream() // 獲取子進程的標(biāo)準(zhǔn)輸入流,如果子進程以`ProcessBuilder`創(chuàng)建,且通過`ProcessBuilder.redirectInput`設(shè)置重定向,那么該方法返回`null` OutputStream getOutputStream()
總結(jié)
到此這篇關(guān)于Java魔法堂:調(diào)用外部程序的文章就介紹到這了,更多相關(guān)Java調(diào)用外部程序內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用SpringBoot與EasyExcel實現(xiàn)復(fù)雜的導(dǎo)入導(dǎo)出
這篇文章主要介紹了使用SpringBoot與EasyExcel實現(xiàn)復(fù)雜的導(dǎo)入導(dǎo)出,EasyExcel是一個快速解決大文件內(nèi)存溢出的Excel處理工具,它能讓你在不用考慮性能、內(nèi)存等因素的情況下,快速完成Excel的讀、寫等功能,需要的朋友可以參考下2023-10-10詳談ThreadLocal-單例模式下高并發(fā)線程安全
這篇文章主要介紹了ThreadLocal-單例模式下高并發(fā)線程安全,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09基于SpringBoot2的Shiro最簡配置操作(兩個文件)
這篇文章主要介紹了基于SpringBoot2的Shiro最簡配置操作(兩個文件),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-01-01