Java程序執(zhí)行Cmd指令所遇問題記錄及解決方案
這篇是有關(guān)在編寫Java程序執(zhí)行Cmd指令時所遇到的問題記錄,其中有一些是個人的理解,如有問題望不吝賜教,感謝❤
Windows 命令提示符(cmd.exe)是 Windows NT 下的一個用于運行 Windows 控制面板程序或某些 DOS 程序的shell程序
1.執(zhí)行Cmd命令的兩種方式
(1)RunTime.getRunTime().exec(多種重載方式) - 會得到一個Process對象通過其start()方法開啟一個新進(jìn)程以執(zhí)行輸入的指令。

這種方法就不多說了,最后這種形式還是用到第二種方式的方法(Java Api文檔中也推薦使用第二種方式去創(chuàng)建一個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.獲取執(zhí)行指令后的輸出:

在這里就遇到點問題,
上面兩種方式執(zhí)行Windows自帶的命令都沒有什么問題(像Ping、Ipconfig)。但是當(dāng)執(zhí)行像“Java -version”這樣的外部命令,其輸出通過getInputStream()方法是拿不到的。
后來是通過參考網(wǎng)上資料,采用將子進(jìn)程的輸出重定向到文件中,再從文件中讀取內(nèi)容的方法:
// 外部程序的輸出放到了錯誤信息輸出流中,不將錯誤信息流輸出到文件話,輸出信息就看不到了😂
pb.redirectErrorStream(true);
// 把執(zhí)行結(jié)果輸出
pb.redirectOutput(file);
//等待語句執(zhí)行完成,否則可能會讀不到結(jié)果。
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開發(fā)文檔中這句“否則,如果使用ProcessBuilder.redirectErrorStream重定向子進(jìn)程的標(biāo)準(zhǔn)錯誤,則此方法返回的輸入流將接收合并的標(biāo)準(zhǔn)輸出和子進(jìn)程的標(biāo)準(zhǔn)錯誤?!保ㄏ旅鎴D片)怎么就沒用呢,結(jié)果回頭一看,文檔是Java 8的,我跑的程序用的是Java 7的,把自己整笑了🙃,還在這一通瞎操作。
而至于為什么要將子進(jìn)程標(biāo)準(zhǔn)輸出和子進(jìn)程的標(biāo)準(zhǔn)錯誤輸出合并,可以看下小弟下面的拙見。
對于非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);
// 等待語句執(zhí)行完成,否則可能會讀不到結(jié)果。
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.關(guān)于getInputStream ()無法得到子進(jìn)程輸出的原因

此方法獲取的流是子進(jìn)程正常輸出流不包括異常錯誤信息流,Process對象將異常信息放在了ErrorStream中。這里可以試一下,會發(fā)現(xiàn)執(zhí)行“Java -version”控制臺輸出的是紅字,也就是異常信息。

emmm至于為什么非Windows自帶命令的正常輸出會被視作異常信息,不太清楚,下次再looklook源碼。
而按上面圖片的最后一句,
4.一個調(diào)用指令例子
private static final String TEMP_FILE_PATH = "D:\\temp.txt";
/**
* 運行外部程序命令 無參數(shù)時調(diào)用
* @param command 輸入命令
* @return 輸出內(nèi)容
*/
public static StringBuilder runOutCmd(String command) {
// 默認(rèn)字符解析GBK
return runOutCmd(command, null,"GBK");
}
/**
* 運行外部程序命令 帶參數(shù)
* @param command 輸入命令
* @param args 輸入?yún)?shù)
* @return
*/
public static StringBuilder runOutCmd(String command, List<String> args) {
// 默認(rèn)字符解析GBK
return runOutCmd(command, args,"GBK");
}
/**
* 運行外部程序命令 - 帶參數(shù)并規(guī)定字符解析格式
* @param args 輸入?yún)?shù)
* @param command 輸入命令
* @param charsetName 輸出字符解析格式
* @return
*/
public static StringBuilder runOutCmd(String command, List<String> args, String charsetName) {
BufferedReader br = null;
StringBuilder outPutResult = new StringBuilder();
try {
// 新建一個用來存儲子進(jìn)程輸出結(jié)果結(jié)果的緩存文件
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);
// 把執(zhí)行結(jié)果輸出
pb.redirectOutput(file);
//等待語句執(zhí)行完成,否則可能會讀不到結(jié)果。
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;
}
最新例子(不用借助文件):
/**
* 運行外部程序命令 - 帶參數(shù)并規(guī)定字符解析格式
*
* @param args 輸入?yún)?shù)
* @param command 輸入命令
* @param charsetName 輸出字符解析格式
* @return
*/
public static StringBuilder runOutCmd(String command, List<String> args, 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);
//等待語句執(zhí)行完成,否則可能會讀不到結(jié)果。
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()作用:

意味著使用此方法,子進(jìn)程的報錯的異常信息也會在當(dāng)前Java進(jìn)程的控制臺輸出,而Process對象將非Windows命令的輸出視為異常信息,那么非Windows命令的輸出當(dāng)使用了此方法的時候會在控制臺輸出。
2.字符解析格式問題:
輸出出現(xiàn)亂碼,與Cmd程序字符格式默認(rèn)為GBK有關(guān)。
按實際情況修改流的解析格式就可以了:
br = new BufferedReader(new InputStreamReader(in,charsetName));

到此這篇關(guān)于Java程序執(zhí)行Cmd指令所遇問題記錄及解決方案的文章就介紹到這了,更多相關(guān)Java程序執(zhí)行Cmd指令內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java數(shù)據(jù)結(jié)構(gòu)之棧的基本定義與實現(xiàn)方法示例
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)之棧的基本定義與實現(xiàn)方法,簡單描述了數(shù)據(jù)結(jié)構(gòu)中棧的功能、原理,并結(jié)合java實例形式分析了棧的基本定義與使用方法,需要的朋友可以參考下2017-10-10
idea指定maven的settings文件不生效的問題解決
本文主要介紹了idea指定maven的settings文件不生效的問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
SpringAOP中的切點表達(dá)式Pointcut詳解
這篇文章主要介紹了SpringAOP中的切點表達(dá)式Pointcut詳解,Spring?的?AOP?中的一個核心概念是切點(Pointcut),切點表達(dá)式定義通知(Advice)執(zhí)行的范圍,需要的朋友可以參考下2023-08-08
Spring?Boot日志SLF4J和Logback示例詳解
這篇文章主要介紹了Spring?Boot日志SLF4J和Logback詳解,Logback相比于Log4j,性能提高了10倍以上的性能,占用的內(nèi)存也變小了,并且文檔十分詳細(xì),推薦使用Slf4j+Logback,需要的朋友可以參考下2023-07-07

