日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java runtime shell_java Runtime.exec()执行shell/cmd命令:常见的几种陷阱与一种完善实现...

發(fā)布時間:2025/4/5 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java runtime shell_java Runtime.exec()执行shell/cmd命令:常见的几种陷阱与一种完善实现... 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Runtime.getRuntime().exec()執(zhí)行JVM之外的程序:常見的幾種陷阱

前言

日常java開發(fā)中,有時需要通過java運行其它應(yīng)用功程序,比如shell命令等。jdk的Runtime類提供了這樣的方法。首先來看Runtime類的文檔, 從文檔中可以看出,每個java程序只會有一個Runtime實例,顯然這是一個單例模式。

/**

* Every Java application has a single instance of class

* Runtime that allows the application to interface with

* the environment in which the application is running. The current

* runtime can be obtained from the getRuntime method.

*

* An application cannot create its own instance of this class.

*/

public class Runtime {

private static Runtime currentRuntime = new Runtime();

/**

* Returns the runtime object associated with the current Java application.

* Most of the methods of class Runtime are instance

* methods and must be invoked with respect to the current runtime object.

*

* @return the Runtime object associated with the current

* Java application.

*/

public static Runtime getRuntime() {

return currentRuntime;

}

/** Don't let anyone else instantiate this class */

private Runtime() {}

......

}

要運行JVM中外的程序,Runtime類提供了如下方法,詳細(xì)使用方法可參見源碼注釋

public Process exec(String command) throws IOException

public Process exec(String cmdarray[]) throws IOException

public Process exec(String command, String[] envp) throws IOException

public Process exec(String command, String[] envp, File dir) throws IOException

public Process exec(String[] cmdarray, String[] envp) throws IOException

public Process exec(String[] cmdarray, String[] envp, File dir) throws IOException

通過這種方式運行外部程序,有幾個陷阱需要注意,本文嘗試總結(jié)常見的幾個陷阱,并給出相應(yīng)的解決方法。同時封裝一種比較完善的工具類,用來運行外部應(yīng)用,并提供超時功能。

Runtime.exec()常見的幾種陷阱以及避免方法

陷阱1:IllegalThreadStateException

通過exec執(zhí)行java命令為例子,最簡單的方式如下。執(zhí)行exec后,通過Process獲取外部進(jìn)程的返回值并輸出。

import java.io.IOException;

/**

* Created by yangjinfeng02 on 2016/4/27.

*/

public class Main {

public static void main(String[] args) {

Runtime runtime = Runtime.getRuntime();

try {

Process process = runtime.exec("java");

int exitVal = process.exitValue();

System.out.println("process exit value is " + exitVal);

} catch (IOException e) {

e.printStackTrace();

}

}

}

很遺憾的是,我們發(fā)現(xiàn)輸出結(jié)果如下,拋出了IllegalThreadStateException異常

Exception in thread "main" java.lang.IllegalThreadStateException: process has not exited

at java.lang.ProcessImpl.exitValue(ProcessImpl.java:443)

at com.baidu.ubqa.agent.runner.Main.main(Main.java:18)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:497)

at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

為什么會拋出IllegalThreadStateException異常?

這是因為外部線程還沒有結(jié)束,這個時候去獲取退出碼,exitValue()方法拋出了異常。看到這里讀者可能會問,為什么這個方法不能阻塞到外部進(jìn)程結(jié)束后再返回呢?確實如此,Process有一個waitFor()方法,就是這么做的,返回的也是退出碼。因此,我們可以用waitFor()方法替換exitValue()方法。

陷阱2:Runtime.exec()可能hang住,甚至死鎖

首先看下Process類的文檔說明

*

By default, the created subprocess does not have its own terminal

* or console. All its standard I/O (i.e. stdin, stdout, stderr)

* operations will be redirected to the parent process, where they can

* be accessed via the streams obtained using the methods

* {@link #getOutputStream()},

* {@link #getInputStream()}, and

* {@link #getErrorStream()}.

* The parent process uses these streams to feed input to and get output

* from the subprocess. Because some native platforms only provide

* limited buffer size for standard input and output streams, failure

* to promptly write the input stream or read the output stream of

* the subprocess may cause the subprocess to block, or even deadlock.

從這里可以看出,Runtime.exec()創(chuàng)建的子進(jìn)程公用父進(jìn)程的流,不同平臺上,父進(jìn)程的stream buffer可能被打滿導(dǎo)致子進(jìn)程阻塞,從而永遠(yuǎn)無法返回。

針對這種情況,我們只需要將子進(jìn)程的stream重定向出來即可。

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

/**

* Created by yangjinfeng02 on 2016/4/27.

*/

public class Main {

public static void main(String[] args) {

Runtime runtime = Runtime.getRuntime();

try {

Process process = runtime.exec("java");

BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(process.getInputStream()));

BufferedReader stderrReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));

String line;

System.out.println("OUTPUT");

while ((line = stdoutReader.readLine()) != null) {

System.out.println(line);

}

System.out.println("ERROR");

while ((line = stderrReader.readLine()) != null) {

System.out.println(line);

}

int exitVal = process.waitFor();

System.out.println("process exit value is " + exitVal);

} catch (IOException e) {

e.printStackTrace();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

陷阱3:不同平臺上,命令的兼容性

如果要在windows平臺上運行dir命令,如果直接指定命令參數(shù)為dir,會提示命令找不到。而且不同版本windows系統(tǒng)上,運行改命令的方式也不一樣。對這宗情況,需要根據(jù)系統(tǒng)版本進(jìn)行適當(dāng)區(qū)分。

String osName = System.getProperty("os.name" );

String[] cmd = new String[3];

if(osName.equals("Windows NT")) {

cmd[0] = "cmd.exe" ;

cmd[1] = "/C" ;

cmd[2] = args[0];

} else if(osName.equals("Windows 95")) {

cmd[0] = "command.com" ;

cmd[1] = "/C" ;

cmd[2] = args[0];

}

Runtime rt = Runtime.getRuntime();

Process proc = rt.exec(cmd);

陷阱4:錯把Runtime.exec()的command參數(shù)當(dāng)做命令行

本質(zhì)上來講,Runtime.exec()的command參數(shù)只是一個可運行的命令或者腳本,并不等效于Shell解器或者Cmd.exe,如果你想進(jìn)行輸入輸出重定向,pipeline等操作,則必須通過程序來實現(xiàn)。不能直接在command參數(shù)中做。例如,下面的例子

Process process = runtime.exec("java -version > a.txt");

這樣并不會產(chǎn)出a.txt文件。要達(dá)到這種目的,需通過編程手段實現(xiàn),如下

import java.io.BufferedReader;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStream;

import java.io.PrintWriter;

/**

* Created by yangjinfeng02 on 2016/4/27.

*/

class StreamGobbler extends Thread {

InputStream is;

String type;

OutputStream os;

StreamGobbler(InputStream is, String type) {

this(is, type, null);

}

StreamGobbler(InputStream is, String type, OutputStream redirect) {

this.is = is;

this.type = type;

this.os = redirect;

}

public void run() {

try {

PrintWriter pw = null;

if (os != null)

pw = new PrintWriter(os);

InputStreamReader isr = new InputStreamReader(is);

BufferedReader br = new BufferedReader(isr);

String line;

while ((line = br.readLine()) != null) {

if (pw != null)

pw.println(line);

System.out.println(type + ">" + line);

}

if (pw != null)

pw.flush();

} catch (IOException ioe) {

ioe.printStackTrace();

}

}

}

public class Main {

public static void main(String args[]) {

try {

FileOutputStream fos = new FileOutputStream("logs/a.log");

Runtime rt = Runtime.getRuntime();

Process proc = rt.exec("cmd.exe /C dir");

// 重定向輸出流和錯誤流

StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");

StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT", fos);

errorGobbler.start();

outputGobbler.start();

int exitVal = proc.waitFor();

System.out.println("ExitValue: " + exitVal);

fos.flush();

fos.close();

} catch (Throwable t) {

t.printStackTrace();

}

}

}

一個比較完善的工具類

下面提供一種比較完善的實現(xiàn),提供了超時功能。

封裝返回結(jié)果

/**

* ExecuteResult.java

*/

import lombok.Data;

import lombok.ToString;

@Data

@ToString

public class ExecuteResult {

private int exitCode;

private String executeOut;

public ExecuteResult(int exitCode, String executeOut) {

this.exitCode = exitCode;

this.executeOut = executeOut;

}

}

對外接口

/**

* LocalCommandExecutor.java

*/

public interface LocalCommandExecutor {

ExecuteResult executeCommand(String command, long timeout);

}

StreamGobbler類,用來完成stream的管理

/**

* StreamGobbler.java

*/

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

public class StreamGobbler extends Thread {

private static Logger logger = LoggerFactory.getLogger(StreamGobbler.class);

private InputStream inputStream;

private String streamType;

private StringBuilder buf;

private volatile boolean isStopped = false;

/**

* @param inputStream the InputStream to be consumed

* @param streamType the stream type (should be OUTPUT or ERROR)

*/

public StreamGobbler(final InputStream inputStream, final String streamType) {

this.inputStream = inputStream;

this.streamType = streamType;

this.buf = new StringBuilder();

this.isStopped = false;

}

/**

* Consumes the output from the input stream and displays the lines consumed

* if configured to do so.

*/

@Override

public void run() {

try {

// 默認(rèn)編碼為UTF-8,這里設(shè)置編碼為GBK,因為WIN7的編碼為GBK

InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "GBK");

BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

String line = null;

while ((line = bufferedReader.readLine()) != null) {

this.buf.append(line + "\n");

}

} catch (IOException ex) {

logger.trace("Failed to successfully consume and display the input stream of type " + streamType + ".", ex);

} finally {

this.isStopped = true;

synchronized (this) {

notify();

}

}

}

public String getContent() {

if (!this.isStopped) {

synchronized (this) {

try {

wait();

} catch (InterruptedException ignore) {

ignore.printStackTrace();

}

}

}

return this.buf.toString();

}

}

實現(xiàn)類

通過SynchronousQueue隊列保證只有一個線程在獲取外部進(jìn)程的退出碼,由線程池提供超時功能。

/**

* LocalCommandExecutorImpl.java

*/

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import java.io.Closeable;

import java.io.IOException;

import java.io.InputStream;

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Future;

import java.util.concurrent.SynchronousQueue;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.TimeoutException;

public class LocalCommandExecutorImpl implements LocalCommandExecutor {

static final Logger logger = LoggerFactory.getLogger(LocalCommandExecutorImpl.class);

static ExecutorService pool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 3L, TimeUnit.SECONDS,

new SynchronousQueue());

public ExecuteResult executeCommand(String command, long timeout) {

Process process = null;

InputStream pIn = null;

InputStream pErr = null;

StreamGobbler outputGobbler = null;

StreamGobbler errorGobbler = null;

Future executeFuture = null;

try {

logger.info(command.toString());

process = Runtime.getRuntime().exec(command);

final Process p = process;

// close process's output stream.

p.getOutputStream().close();

pIn = process.getInputStream();

outputGobbler = new StreamGobbler(pIn, "OUTPUT");

outputGobbler.start();

pErr = process.getErrorStream();

errorGobbler = new StreamGobbler(pErr, "ERROR");

errorGobbler.start();

// create a Callable for the command's Process which can be called by an Executor

Callable call = new Callable() {

public Integer call() throws Exception {

p.waitFor();

return p.exitValue();

}

};

// submit the command's call and get the result from a

executeFuture = pool.submit(call);

int exitCode = executeFuture.get(timeout, TimeUnit.MILLISECONDS);

return new ExecuteResult(exitCode, outputGobbler.getContent());

} catch (IOException ex) {

String errorMessage = "The command [" + command + "] execute failed.";

logger.error(errorMessage, ex);

return new ExecuteResult(-1, null);

} catch (TimeoutException ex) {

String errorMessage = "The command [" + command + "] timed out.";

logger.error(errorMessage, ex);

return new ExecuteResult(-1, null);

} catch (ExecutionException ex) {

String errorMessage = "The command [" + command + "] did not complete due to an execution error.";

logger.error(errorMessage, ex);

return new ExecuteResult(-1, null);

} catch (InterruptedException ex) {

String errorMessage = "The command [" + command + "] did not complete due to an interrupted error.";

logger.error(errorMessage, ex);

return new ExecuteResult(-1, null);

} finally {

if (executeFuture != null) {

try {

executeFuture.cancel(true);

} catch (Exception ignore) {

ignore.printStackTrace();

}

}

if (pIn != null) {

this.closeQuietly(pIn);

if (outputGobbler != null && !outputGobbler.isInterrupted()) {

outputGobbler.interrupt();

}

}

if (pErr != null) {

this.closeQuietly(pErr);

if (errorGobbler != null && !errorGobbler.isInterrupted()) {

errorGobbler.interrupt();

}

}

if (process != null) {

process.destroy();

}

}

}

private void closeQuietly(Closeable c) {

try {

if (c != null) {

c.close();

}

} catch (IOException e) {

logger.error("exception", e);

}

}

}

總結(jié)

以上是生活随笔為你收集整理的java runtime shell_java Runtime.exec()执行shell/cmd命令:常见的几种陷阱与一种完善实现...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。