Java调⽤linux命令及Shell脚本
Java可以通过Runtime().exec()⽅法调⽤linux平台下的命令及Shell脚本。
获取命令执⾏结果通常有两种,⼀种是waitfor⽅法,另⼀种是exitValue。
但waitfor⽅法可能造成阻塞,原因如下:
当调⽤exec⽅法后,JVM启动⼀个⼦进程,该进程会与JVM进程建⽴3个管道连接,即标准输⼊流、标准输出流、错误错误流。假设该程序不间断向标准输出流和标准错误流写数据,⽽JVM不读取,那么数据会暂存在Linux缓冲区中,缓冲区写满后该程序将⽆法继续写⼊,程序就会⼀直阻塞在waitfor⽅法,永远⽆法结束。
解决⽅法就是增加两个线程,⼀个负责读取标准输出流,⼀个负责读取标准错误流,这样数据就不会积压在缓冲区,waitfor⽅法可以正常结束。
总之,调⽤外部程序时需要注意以下两点:
1、如果外部程序有⼤量输出,需要有单独线程读取输出流和错误流
2、必须关闭3个句柄——标准输⼊流、标准输出流、标准错误流
考虑到阻塞问题以及为了获取命令输出,⽂中使⽤了exitValue⽅法。
代码如下
ShellUtils:执⾏外部命令的⼯具类
package com.wll.shell;
import com.wll.utils.CommonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class ShellUtils {
private static final Logger LOGGER = Logger(ShellUtils.class);
private static final long THREAD_SLEEP_TIME = 10;
private static final int DEFAULT_WAIT_TIME = 20 * 60 * 1000;
public static void runShell(String cmd) {
String[] command = new String[]{"/bin/sh", "-c", cmd};
try {
Process process = Runtime().exec(command);
ShellResult result = getProcessResult(process, DEFAULT_WAIT_TIME);
LOGGER.info("Command [{}] executed successfully.", cmd);
LOGGER.String());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取命令执⾏结果
* @param process ⼦进程
* @param waitTime 指定超时时间
* @return 命令执⾏输出结果
*/
public static ShellResult getProcessResult(Process process, long waitTime) {
ShellResult cmdResult = new ShellResult();
boolean isTimeout = false;
long loopNumber = waitTime / THREAD_SLEEP_TIME;
long loopNumber = waitTime / THREAD_SLEEP_TIME;
long realLoopNumber = 0;
int exitValue = -1;
StreamGobbler errorGobbler = new ErrorStream());        StreamGobbler outputGobbler = new InputStream());
errorGobbler.start();
outputGobbler.start();
try {
while (true) {
try {
Thread.sleep(THREAD_SLEEP_TIME);
exitValue = itValue();
break;
} catch (InterruptedException e) {
realLoopNumber++;
if (realLoopNumber >= loopNumber) {
isTimeout = true;
break;
}
}
}
errorGobbler.join();
outputGobbler.join();
if (isTimeout) {
cmdResult.setErrorCode(ShellResult.TIMEOUT);
return cmdResult;
}
cmdResult.setErrorCode(exitValue);
if (exitValue != ShellResult.SUCCESS) {
cmdResult.Output());
} else {
cmdResult.Output());
}
} catch (InterruptedException e) {
<("Get shell result error.");
cmdResult.setErrorCode(ShellResult.ERROR);
} finally {
CommonUtils.ErrorStream());
CommonUtils.InputStream());
CommonUtils.OutputStream());
}
return cmdResult;
}
}
StreamGobbler:读取命令输出流和错误流的⼯具类
linux循环执行命令脚本
import com.wll.utils.CommonUtils;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class StreamGobbler extends Thread {
private InputStream is;
private List<String> output = new ArrayList<String>();
public StreamGobbler(InputStream is) {
this.is = is;
}
public List<String> getOutput() {
return output;
}
@Override
public void run() {
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));            String line = "";
while ((line = adLine()) != null) {
output.add(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
CommonUtils.closeStream(reader);
}
}
}
ShellResult:命令执⾏结果
import java.util.List;
public class ShellResult {
public static final int SUCCESS = 0;
public static final int ERROR = 1;
public static final int TIMEOUT = 13;
private int errorCode;
private List<String> description;
public int getErrorCode() {
return errorCode;
}
public void setErrorCode(int errorCode) {
}
public List<String> getDescription() {
return description;
}
public void setDescription(List<String> description) {
this.description = description;
}
@Override
public String toString() {
return "ShellResult{" +
"errorCode=" + errorCode +
", description=" + description +
'}';
}
}
ShellTest:测试类
package com.wll.shell;
public class ShellTest {
public static void main(String[] args) {
String cmd = "";
if (args.length == 1) {
cmd = args[0];
}
ShellUtils.runShell(cmd);
}
}
另外,由于流关闭操作⽤得⽐较频繁,故单独写了个⼯具类。
import org.apache.log4j.Logger;
import java.io.Closeable;
import java.io.IOException;
public class CommonUtils {
private static final Logger LOGGER = Logger(CommonUtils.class);
/**
* 提供统⼀关闭流的⽅法
*
* @param stream 待关闭的流
*/
public static void closeStream(Closeable stream) {
if (stream == null) {
return;
}
try {
stream.close();
} catch (IOException e) {
<("Close stream failed!");
}
}
}
代码放在CentOS下,详细⽬录结构如下:
以下为测试脚本,⼗分简单,只是输出当前⽇期和时间
最终运⾏结果如下:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。