好久没有写博客,今天在这里简单介绍一下我在做软件工程项目时使用Java Runtime.exec方法遇到的一些问题,希望加深大家对Runtime.exec方法的理解。

首先,大家都知道,Runtime.exec(String command)函数是用来执行命令行命令的,那么是不是我把写在命令行中的命令直接复制粘贴成参数就可以了呢?事实并非如此,我们要理解这个函数的使用,要从它的源码说起。

public Process exec(String command, String[] envp, File dir)
        throws IOException {
        if (command.length() == 0)
            throw new IllegalArgumentException("Empty command");

        StringTokenizer st = new StringTokenizer(command);
        String[] cmdarray = new String[st.countTokens()];
        for (int i = 0; st.hasMoreTokens(); i++)
            cmdarray[i] = st.nextToken();
        return exec(cmdarray, envp, dir);
    }

看到上面这个函数,我们发现这个函数的执行最后归结为Runtime.exec(String[] cmdarray)的执行。那么,command是如何转化成cmdarray的呢?很简单,我们相当于执行了一个split(" ")函数,将其转化为了字符串数组,再执行。那么,这和我们直接写命令行命令有什么区别呢?写简单命令的时候,区别是不大的,但是当我们的命令变复杂的时候,就不是这么一回事了。

我们来看下面这个命令

            String command = "docker exec 3ba171ece612 sh -c 'output/libJudgerSandbox.so --exe_path="+exe+" --input_path="+in+" --output_path="+out+" --error_path="+error+" --max_real_time="+timeLimit+" --max_memory="+memoryLimit+"'";

这个命令就很复杂了,简单来说,我们调起一个docker,在docker内部执行一个脚本命令,sh -c后面跟的就是要在dokcer内部执行的脚本命令。最开始的时候,我就是把这整个命令丢尽了exec函数,但是怎么执行都不成功,直到我看到了相关源码,我才明白,如果以空格为单位进行分割,我的上述代码当然是不可能被执行成功的。因此选择直接用String[] cmdarray的形式执行命令。

String command = "output/libJudgerSandbox.so --exe_path="+exe+" --input_path="+in+" --output_path="+out+" --error_path="+error+" --max_real_time="+timeLimit+" --max_memory="+memoryLimit;
String[] commands = {"docker","exec","3ba171ece612","sh","-c",command};
Process p = Runtime.getRuntime().exec(commands);

如此这一段命令便可以正确运行了。在正确运行之前,还遇到了一个小bug,按照sh -c的要求,后面的命令要放到单引号之间才能正确运行,但是在exec中,同年过cmdarry的形式,我们已经把后面的shell命令与前面的命令完全隔离开了,因此不需要加单引号。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐