How to Run a Shell Command in Java – 如何在Java中运行一个Shell命令

最后修改: 2016年 8月 24日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

With this tutorial we’ll illustrate the two ways of executing a shell command from within Java code.

通过本教程,我们将说明Java代码中执行shell命令的两种方式

The first is to use the Runtime class and call its exec method.

首先是使用Runtime类并调用其exec方法。

The second and more customizable way, will be to create and use a ProcessBuilder instance.

第二种也是更可定制的方式,将是创建和使用一个ProcessBuilder实例。

2. Operating System Dependency

2.操作系统的依赖性

Before we’re going to create a new Process executing our shell command, we need to first determine the operating system on which our JVM is running.

在我们创建一个新的Process执行我们的shell命令之前,我们需要首先确定我们的JVM运行在哪个操作系统上。

That’s because, on Windows, we need to run our command as argument to the cmd.exe shell and on all other operating systems we can issue a standard shell, called sh:

这是因为,在Windows上,我们需要将我们的命令作为参数运行到cmd.exe shell,而在所有其他操作系统上,我们可以发出一个标准shell,称为sh:

boolean isWindows = System.getProperty("os.name")
  .toLowerCase().startsWith("windows");

3. Input and Output

3.输入和输出

Furthermore we need a way to hook into the input and output streams of our process.

此外,我们需要一种方法来钩住我们进程的输入和输出流。

At least the output must be consumed – otherwise our process doesn’t return successfully, instead it will hang.

至少输出必须被消耗掉–否则我们的进程不会成功返回,反而会挂掉。

Let’s implement a commonly used class called StreamGobbler which consumes an InputStream:

让我们来实现一个常用的类,叫做StreamGobbler,它消耗一个InputStream

private static class StreamGobbler implements Runnable {
    private InputStream inputStream;
    private Consumer<String> consumer;

    public StreamGobbler(InputStream inputStream, Consumer<String> consumer) {
        this.inputStream = inputStream;
        this.consumer = consumer;
    }

    @Override
    public void run() {
        new BufferedReader(new InputStreamReader(inputStream)).lines()
          .forEach(consumer);
    }
}

NOTE: This class is implementing the Runnable interface, which means that it could be executed by any Executor.

注意:该类实现了Runnable接口,这意味着它可以被任何Executor所执行。

4. Runtime.exec()

4.Runtime.exec()

A method-call to Runtime.exec() is a simple, not yet customizable, way to spawn a new sub-process.

Runtime.exec()的方法调用是催生一个新的子进程的简单方法,但还不能定制。

In the following example we will request a directory-listing of a users home-directory and printing it to the console:

在下面的例子中,我们将请求一个用户的家庭目录列表并将其打印到控制台。

String homeDirectory = System.getProperty("user.home");
Process process;
if (isWindows) {
    process = Runtime.getRuntime()
      .exec(String.format("cmd.exe /c dir %s", homeDirectory));
} else {
    process = Runtime.getRuntime()
      .exec(String.format("sh -c ls %s", homeDirectory));
}
StreamGobbler streamGobbler = 
  new StreamGobbler(process.getInputStream(), System.out::println);
Future<?> future = Executors.newSingleThreadExecutor().submit(streamGobbler);

int exitCode = process.waitFor();
assert exitCode == 0;

future.get(); // waits for streamGobbler to finish

When you want to use the result of the process, make sure to call the future.get() method to wait for the computation to complete.

当你想使用这个过程的结果时,确保调用 future.get()方法来等待计算完成。

5. ProcessBuilder

5.ProcessBuilder

For the second implementation of our computing problem, we’ll be using a ProcessBuilder. This is preferred over the Runtime approach because we’re able to customize some details.

对于我们计算问题的第二个实现,我们将使用一个ProcessBuilder。这比Runtime方法更受欢迎,因为我们能够定制一些细节。

For example we’re able to:

例如,我们能够。

  • change the working directory our shell command is running in using builder.directory()
  • set-up a custom key-value map as environment using builder.environment()
  • redirect input and output streams to custom replacements
  • inherit both of them to the streams of the current JVM process using builder.inheritIO()
ProcessBuilder builder = new ProcessBuilder();
if (isWindows) {
    builder.command("cmd.exe", "/c", "dir");
} else {
    builder.command("sh", "-c", "ls");
}
builder.directory(new File(System.getProperty("user.home")));
Process process = builder.start();
StreamGobbler streamGobbler = 
  new StreamGobbler(process.getInputStream(), System.out::println);
Future<?> future = Executors.newSingleThreadExecutor().submit(streamGobbler);
int exitCode = process.waitFor();
assert exitCode == 0;
future.get(10, TimeUnit.SECONDS)

6. Conclusion

6.结论

As we’ve seen in this quick tutorial, we can execute a shell command in Java in two distinct ways.

正如我们在这个快速教程中所看到的,我们可以通过两种不同的方式在Java中执行一个shell命令。

Generally, if you’re planning to customize the execution of the spawned process, for example, to change its working directory, you should consider using a ProcessBuilder.

一般来说,如果你打算自定义生成的进程的执行,例如,改变其工作目录,你应该考虑使用ProcessBuilder

As always, you’ll find the sources on GitHub.

像往常一样,你可以在GitHub上找到源代码