Java程式執行Cmd指令所遇問題記錄及解決方案
這篇是有關在編寫Java程式執行Cmd指令時所遇到的問題記錄,其中有一些是個人的理解,如有問題望不吝賜教,感謝❤
Windows 命令提示符(cmd.exe)是 Windows NT 下的一個用於執行 Windows 控制面板程式或某些 DOS 程式的shell程式
1.執行Cmd命令的兩種方式
(1)RunTime.getRunTime().exec(多種過載方式) - 會得到一個Process物件通過其start()方法開啟一個新程序以執行輸入的指令。
這種方法就不多說了,最後這種形式還是用到第二種方式的方法(Java Api文件中也推薦使用第二種方式去建立一個Process物件
* @see ProcessBuilder * @since 1.3 */ public Process exec(String[] cmdarray,String[] envp,File dir) throws IOException { return new ProcessBuilder(cmdarray) .environment(envp) .directory(dir) .start(); }
(2).new ProcessBuilder().command(指令)
2.獲取執行指令後的輸出:
在這裡就遇到點問題,
上面兩種方式執行Windows自帶的命令都沒有什麼問題(像Ping、Ipconfig)。但是當執行像“Java -version”這樣的外部命令,其輸出通過getInputStream()方法是拿不到的。
後來是通過參考網上資料,採用將子程序的輸出重定向到檔案中,再從檔案中讀取內容的方法:
// 外部程式的輸出放到了錯誤資訊輸出流中,不將錯誤資訊流輸出到檔案話,輸出資訊就看不到了😂 pb.redirectErrorStream(true); // 把執行結果輸出 pb.redirectOutput(file); //等待語句執行完成,否則可能會讀不到結果。 pb.start().waitFor(); InputStream in = new FileInputStream(file); br = new BufferedReader(new InputStreamReader(in,charsetName)); String line = null; while ((line = br.readLine()) != null) { outPutResult.append(line).append("\n"); } br.close(); br = null; // 刪除臨時檔案 file.delete();
最新解決方法:剛寫完這篇部落格,就在想Java開發文件中這句“否則,如果使用ProcessBuilder.redirectErrorStream重定向子程序的標準錯誤,則此方法返回的輸入流將接收合併的標準輸出和子程序的標準錯誤。”(下面圖片)怎麼就沒用呢,結果回頭一看,文件是Java 8的,我跑的程式用的是Java 7的,把自己整笑了🙃,還在這一通瞎操作。
而至於為什麼要將子程序標準輸出和子程序的標準錯誤輸出合併,可以看下小弟下面的拙見。
對於非Windows自帶命令,可以這樣寫(不再需要藉助檔案):
public static StringBuilder runOutCmdTest(String command) { BufferedReader br = null; StringBuilder outPutResult = new StringBuilder(); try{ ProcessBuilder pb = new ProcessBuilder().command("cmd.exe","/c",command); // 外部程式的輸出放到了錯誤資訊輸出流中 pb.redirectErrorStream(true); // 等待語句執行完成,否則可能會讀不到結果。 Process process = pb.start(); process.waitFor(); InputStream inputStream = process.getInputStream(); br = new BufferedReader(new InputStreamReader(inputStream,"GBK")); String line; while ((line = br.readLine()) != null) { outPutResult.append(line).append("\n"); } br.close(); br = null; } catch (Exception e) { e.printStackTrace(); } return outPutResult; }
3.關於getInputStream ()無法得到子程序輸出的原因
此方法獲取的流是子程序正常輸出流不包括異常錯誤資訊流,Process物件將異常資訊放在了ErrorStream中。這裡可以試一下,會發現執行“Java -version”控制檯輸出的是紅字,也就是異常資訊。
emmm至於為什麼非Windows自帶命令的正常輸出會被視作異常資訊,不太清楚,下次再looklook原始碼。
而按上面圖片的最後一句,
4.一個呼叫指令例子
private static final String TEMP_FILE_PATH = "D:\\temp.txt"; /** * 執行外部程式命令 無引數時呼叫 * @param command 輸入命令 * @return 輸出內容 */ public static StringBuilder runOutCmd(String command) { // 預設字元解析GBK return runOutCmd(command,null,"GBK"); } /** * 執行外部程式命令 帶引數 * @param command 輸入命令 * @param args 輸入引數 * @return */ public static StringBuilder runOutCmd(String command,List<String> args) { // 預設字元解析GBK return runOutCmd(command,args,"GBK"); } /** * 執行外部程式命令 - 帶引數並規定字元解析格式 * @param args 輸入引數 * @param command 輸入命令 * @param charsetName 輸出字元解析格式 * @return */ public static StringBuilder runOutCmd(String command,List<String> args,String charsetName) { BufferedReader br = null; StringBuilder outPutResult = new StringBuilder(); try { // 新建一個用來儲存子程序輸出結果結果的快取檔案 File file = new File(TEMP_FILE_PATH); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } if (!file.exists()) { file.createNewFile(); } List<String> execCommand = new LinkedList<>(); if (args != null) { execCommand.addAll(args); } execCommand.add(0,command); execCommand.add(0,"/c"); execCommand.add(0,"cmd.exe"); ProcessBuilder pb = new ProcessBuilder().command(execCommand).inheritIO(); // 外部程式的輸出放到了錯誤資訊輸出流中 pb.redirectErrorStream(true); // 把執行結果輸出 pb.redirectOutput(file); //等待語句執行完成,否則可能會讀不到結果。 pb.start().waitFor(); InputStream in = new FileInputStream(file); br = new BufferedReader(new InputStreamReader(in,charsetName)); String line = null; while ((line = br.readLine()) != null) { outPutResult.append(line).append("\n"); } br.close(); br = null; // 刪除臨時檔案 file.delete(); } catch (Exception e) { e.printStackTrace(); } finally { if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } return outPutResult; }
最新例子(不用藉助檔案):
/** * 執行外部程式命令 - 帶引數並規定字元解析格式 * * @param args 輸入引數 * @param command 輸入命令 * @param charsetName 輸出字元解析格式 * @return */ public static StringBuilder runOutCmd(String command,String charsetName) { BufferedReader br = null; StringBuilder outPutResult = new StringBuilder(); try { List<String> execCommand = new LinkedList<>(); if (args != null) { execCommand.addAll(args); } execCommand.add(0,command); execCommand.add(0,"/c"); execCommand.add(0,"cmd.exe"); ProcessBuilder pb = new ProcessBuilder().command(execCommand).inheritIO(); // 外部程式的輸出放到了錯誤資訊輸出流中 pb.redirectErrorStream(true); //等待語句執行完成,否則可能會讀不到結果。 Process process = pb.start(); process.waitFor(); InputStream inputStream = process.getInputStream(); br = new BufferedReader(new InputStreamReader(inputStream,charsetName)); String line; while ((line = br.readLine()) != null) { outPutResult.append(line).append("\n"); } br.close(); br = null; } catch (Exception e) { e.printStackTrace(); } finally { if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } return outPutResult; }
ps:
1. inheritIO()作用:
意味著使用此方法,子程序的報錯的異常資訊也會在當前Java程序的控制檯輸出,而Process物件將非Windows命令的輸出視為異常資訊,那麼非Windows命令的輸出當使用了此方法的時候會在控制檯輸出。
2.字元解析格式問題:
輸出出現亂碼,與Cmd程式字元格式預設為GBK有關。
按實際情況修改流的解析格式就可以了:
br = new BufferedReader(new InputStreamReader(in,charsetName));
到此這篇關於Java程式執行Cmd指令所遇問題記錄及解決方案的文章就介紹到這了,更多相關Java程式執行Cmd指令內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!