使用java创建新的进程

使用jdk内置的工具

import org.apache.commons.io.IOUtils;
import java.nio.charset.Charset;

public class TestProcess {
    public static void main(String[] args) throws Exception {
        testExec();
    }

    private static void testExec() throws Exception {
        String commandPath = "/bin/ls";
        String param = "/Users/xxx/testjars";

        String fullCommand = new StringBuilder()
                .append(commandPath)
                .append(" ")
                .append(param)
                .toString();
        Process process = Runtime.getRuntime().exec(fullCommand);
        int exitCode = process.waitFor();
        // 状态码0表示执行成功
        if (exitCode == 0) {
            String result = IOUtils.toString(process.getInputStream(), Charset.forName("GBK"));
            System.out.println(result);
        } else {
            String errMsg = IOUtils.toString(process.getErrorStream(), Charset.forName("GBK"));
            System.out.println(errMsg);
        }
    }

}

Runtime 的 exec() 方法内部会将完整命令使用 空格或者换行 分割为多个,第一个当作执行命令,后面的当作执行参数。内部使用 ProcessBuilder 来创建 Process。

import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;

public class TestProcess2 {
    public static void main(String[] args) throws Exception {
        testExec();
    }

    private static void testExec() throws IOException, InterruptedException {
        String commandPath = "/bin/ls";
        String param = "/Users/xxx/testjars";
        List<String> commands = Arrays.asList(commandPath, param);
        Process process = new ProcessBuilder().command(commands).start();
        int exitCode = process.waitFor();
        // 状态码0表示执行成功
        if (exitCode == 0) {
            String result = IOUtils.toString(process.getInputStream(), Charset.forName("GBK"));
            System.out.println(result);
        } else {
            String errMsg = IOUtils.toString(process.getErrorStream(), Charset.forName("GBK"));
            System.out.println(errMsg);
        }
    }
}

和上面类似,直接使用 ProcessBuilder 来创建 Process。注意,这个时候,command() 方法不能直接传一个完整命令,如下所示

private static void testExec2() throws IOException, InterruptedException {
        String commandPath = "/bin/ls";
        String param = "/Users/xxx/testjars";

        String fullCommand = new StringBuilder()
                .append(commandPath)
                .append(" ")
                .append(param)
                .toString();
        List<String> commands = Arrays.asList(fullCommand);
        Process process = new ProcessBuilder().command(commands).start();
        int exitCode = process.waitFor();
        ...
    }

这种情况会报错

Exception in thread "main" java.io.IOException: Cannot run program "/bin/ls /Users/xxx/testjars": error=2, No such file or directory
	at java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)
	at com.imooc.TestProcess2.testExec2(TestProcess2.java:41)
	at com.imooc.TestProcess2.main(TestProcess2.java:12)
Caused by: java.io.IOException: error=2, No such file or directory
	at java.lang.UNIXProcess.forkAndExec(Native Method)
	at java.lang.UNIXProcess.<init>(UNIXProcess.java:247)
	at java.lang.ProcessImpl.start(ProcessImpl.java:134)
	at java.lang.ProcessBuilder.start(ProcessBuilder.java:1029)
	... 2 more

它是把 完整命令当作 执行命令了,就会报找不到这个命令(/bin/ls /Users/xxx/testjars)。

使用apache的commons-exec工具

引入依赖

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-exec</artifactId>
    <version>1.3</version>
</dependency>

代码如下

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.PumpStreamHandler;

import java.io.ByteArrayOutputStream;

public class TestCommonsExec {
    public static void main(String[] args) throws Exception {
        testExec();
    }

    private static void testExec() throws Exception {
        String commandPath = "/bin/ls";
        String param = "/Users/zzshi/szz_files/testjars";

        String fullCommand = new StringBuilder()
                .append(commandPath)
                .append(" ")
                .append(param)
                .toString();
        //接收正常结果流
        ByteArrayOutputStream susStream = new ByteArrayOutputStream();
        //接收异常结果流
        ByteArrayOutputStream errStream = new ByteArrayOutputStream();
        CommandLine commandLine = CommandLine.parse(fullCommand);
        DefaultExecutor exec = new DefaultExecutor();
        PumpStreamHandler streamHandler = new PumpStreamHandler(susStream, errStream);
        exec.setStreamHandler(streamHandler);
        int exitCode = exec.execute(commandLine);
        // 状态码0表示执行成功
        if (exitCode == 0) {
            String result = susStream.toString("GBK");
            System.out.println(result);
        } else {
            String errMsg = errStream.toString("GBK");
            System.out.println(errMsg);
        }
    }
}

使用 commons-exec 更加的方便,且支持异步、超时取消等更多功能,使用 ExecuteWatchdog 来实现超时取消(内部创建一个线程一直监听)。Watchdog也就是看门狗,Redission的分布式锁中也有相同的角色。

参考

程序员的福音 - Apache Commons Exec

posted @ 2024-03-16 13:42  strongmore  阅读(59)  评论(0编辑  收藏  举报