使用java建立新的程序

strongmore發表於2024-03-16

使用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

相關文章