一、java本身API执行CMD
Process ps = Runtime.getRuntime().exec(cmd);
ps.waitFor();//等待线程结束
waitFor是为了保证cmd命令在后续任务执行前就完成。但是如果cmd命令存在不少的输出信息或者错误信息,在执行waitFor时,会发生阻塞。
为什么会堵塞呢?原因是当调用exec(cmd)后,JVM会启动一个子进程,该进程会与JVM进程建立3个管道连接,标准输入,标准输出和标准错误流。假设该程序不断在向标准输出流和标准错误流写数据,而JVM不读取,数据会暂时缓冲在Linux的缓冲区,缓冲区满后该程序将无法继续写数据,所以Java程序就会堵塞在waitfor(),永远无法结束。
解决办法就是增加两个线程,一个线程负责读标准输出流,另一个负责读标准错误流,这样子数据就不会积压在缓冲区,程序就能够顺利运行。不一定两个线程都需要,可以根据CMD命令的实际情况,是输出流导致阻塞,还是错误流导致的。
处理输出流的公共线程类:
public class ProcessStreamHandle extends Thread{
private InputStream is;
private List list;//用于保存输出流信息,方便程序读取使用
public ProcessStreamHandle(InputStream is,List list) {
this.is = is;
this.list = list;
}
@Override
public void run() {
BufferedReader br1 = new BufferedReader(new InputStreamReader(is));
try {
String line1 = null;
while ((line1 = br1.readLine()) != null) {
list.add(line1);
}
}catch (IOException e) {
e.printStackTrace();
}finally{
try {
is.close();//关闭通道,避免因过多通道未关而产生:java.io.IOException: Too many open files异常
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
执行CMD命令主程序:
public class JavaCmd {
public static void main(String[] args) {
test();
}
public static void test(){
String rootPath2 = "\""+"D:\\Program Files (x86)\\cwRsync\\bin";//在含空格路径前后加双引号
String cmd = rootPath2+File.separator+"rsync.exe"+"\""+" -vr --delete --progress --ignore-errors /cygdrive/E/test/rsync/a/ /cygdrive/E/test/rsync/b ";
try {
Process ps = Runtime.getRuntime().exec(cmd);
//获取进程的标准输入流
final InputStream is1 = ps.getInputStream();
List inputStrList = new ArrayList();
//获取进城的错误流
final InputStream is2 = ps.getErrorStream();
List errorStrList = new ArrayList();
//启动两个线程,一个线程负责读标准输出流,另一个负责读标准错误流
new ProcessStreamHandle(is1,inputStrList).start();
new ProcessStreamHandle(is2,errorStrList).start();
ps.waitFor();//等待线程结束
System.out.println("over");
printList(inputStrList);
printList(errorStrList);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void printList(List list){
for (String string : list) {
System.out.println(string);
}
}
}
注:
1、cmd命令如果包含空格将无法正确执行。解决办法就是:在执行路径的路径前后添加双引号(" \" ")。
2、waitFor只适用于能够结束的线程,比如你使用命令启动mysql服务,启动程序确实执行完毕,但是线程任然在执行中,就是你的mysql线程。所以使用waitFor要慎重。
二、Commons-IO下的Ant包
public class AntCmdTest {
public static void main(String[] args) {
try {
test();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void test() throws IOException{
ByteArrayOutputStream bOutputStream = new ByteArrayOutputStream();
ExecuteWatchdog wdog = new ExecuteWatchdog(2000l);//cmd命令最大执行时间
Execute exec = new Execute(new PumpStreamHandler(bOutputStream), wdog);
String cmd[]={"notepad ","E:\\\\1.txt"};//使用notepad打开1.txt
exec.setCommandline(cmd);
int exitStatus = exec.execute();
if(exitStatus==0&&!wdog.killedProcess()){
System.out.println((new StringBuilder()).append(Arrays.asList(cmd)).append(" :").append(bOutputStream).toString());
}
String cmd2[] = {"\"D:\\Program Files (x86)\\cwRsync\\bin\\rsync.exe\"","-vr","--delete","/cygdrive/E/test/rsync/a/","/cygdrive/E/test/rsync/b "};
exec.setCommandline(cmd2);
int exitStatus2 = exec.execute();
if(exitStatus2==0&&!wdog.killedProcess()){
System.out.println((new StringBuilder()).append(Arrays.asList(cmd2)).append(" :").append(bOutputStream).toString());
}
}
}
注:
1、ExecuteWatchdog中设置了执行最大时间,得确保你的CMD命令能够执行完毕,所以稍微设置大些。
2、第一个cmd:1.txt将被notepad打开,2秒后关闭;第二个也执行2秒,如果文件过多将无法同步完全。
三、其他
数组执行:Linux+Windows
exec.Command("/bin/sh", "-c", cmd)
exec.Command("cmd", "/C", cmd)