美文网首页
使用Process执行外部命令

使用Process执行外部命令

作者: 毛小力 | 来源:发表于2018-09-18 17:56 被阅读0次

1. Runtime

使用Runtime方法执行外部命令

// 获取当前Runtime
public static Runtime getRuntime();

// 执行命令,如"ping baidu.com"
public Process exec(String command) throws IOException;

// 将命令拆成数组形式,如{"ping", "baidu.com"}
public Process exec(String cmdarray[]) throws IOException;

// 执行命令,指定环境变量(name=value形式)
public Process exec(String command, String[] envp) throws IOException;

// 执行命令,指定环境变量、工作目录(默认继承当前进程的工作目录)
public Process exec(String command, String[] envp, File dir) throws IOException;

2. ProcessBuilder

也是使用ProcessBuilder执行

// 指定命令
public ProcessBuilder command(String... command);

// 不能指定环境变量,只能通过Runtime.exec
ProcessBuilder environment(String[] envp);

// 指定工作目录
public ProcessBuilder directory(File directory);

// 创建子进程执行
public Process start() throws IOException;

3. Process

进程接口。Runtime.exec()方法创建子进程,通过返回的Process实例获取执行情况。

3.1 输入输出

子进程没有自己的终端和控制台。它的所有标准 io(即 stdin,stdout,stderr)操作都将通过三个流 (getOutputStream(),getInputStream(),getErrorStream()) 重定向到父进程。父进程使用这些流来提供到子进程的输入和获得从子进程的输出。
因为有些本机平台仅针对标准输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流出现失败,则可能导致子进程阻塞,甚至产生死锁。

// 向子进程提供stdin
public abstract OutputStream getOutputStream();
// 读取子进程stdout
public abstract InputStream getInputStream();
// 读取子进程stderr
public abstract InputStream getErrorStream();

也可以使用ProcessBuilder重定向子进程的标准IO。

// 重定向stdin
public ProcessBuilder redirectInput(File file);
// 重定向stdout
public ProcessBuilder redirectOutput(File file);
// 重定向stderr
public ProcessBuilder redirectError(File file);

3.2 执行状态

// 阻塞等待子进程执行完毕
public abstract int waitFor() throws InterruptedException;

// 超时等待子进程执行完毕
public boolean waitFor(long timeout, TimeUnit unit) throws InterruptedException;

// 非阻塞获取子进程退出值,0表示正常结束
// 若子进程并没有终止将抛出IllegalThreadStateException
public abstract int exitValue();

// 非阻塞检查子进程存活状态
public boolean isAlive();

3.3 终止进程

public abstract void destroy();

public Process destroyForcibly();

4. 示例

4.1 执行ping

// 指定命令
Process process = Runtime.getRuntime().exec("ping baidu.com");
// 阻塞等待
process.waitFor();
// 打印退出值
System.out.println(process.exitValue());

4.2 获取进程执行输出

先定义一个通用输入流处理函数:

private static final Consumer<InputStream> consumer = (InputStream inputStream) -> {
    BufferedReader bufferedReader = null;
    try {
        bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "gbk"));
        String line = null;
        while ((line = bufferedReader.readLine()) != null) {
            System.out.println(line);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (bufferedReader != null) {
            try {
                bufferedReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
};

改造程序:

Process process = Runtime.getRuntime().exec("ping baidu.com");
consumer.accept(process.getInputStream());
process.waitFor();
System.out.println(process.exitValue());

结果如图:


image.png

4.3 并发读取输出流和错误流

由于输出流和错误流缓冲区空间有限,如果不及时消费,会导致写操作阻塞,进程夯住。

hang.sh:写stdout和stderr

#!/bin/bash

for i in {1..1000}
do
    echo "stdout hang................" >&1
done

for i in {1..1000}
do
    echo "stderr hang................" >&2
done

对于stdout和stderr都有写入的情况,如果串行读取或只读取一个,会导致进程夯住。

Process process = Runtime.getRuntime().exec("bash D:\\Temp\\Process\\hang.sh");
consumer.accept(process.getInputStream());
consumer.accept(process.getErrorStream());
process.waitFor();
System.out.println(process.exitValue());

需要对流并发读取:

Process process = Runtime.getRuntime().exec("bash D:\\Temp\\Process\\hang.sh");
new Thread(() -> consumer.accept(process.getInputStream())).start();
new Thread(() -> consumer.accept(process.getErrorStream())).start();
process.waitFor();
System.out.println(process.exitValue());

4.4 重定向标准流

ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.directory(new File("D:\\Temp\\Process")); // 设置工作目录
processBuilder.command("bash", "redirect.sh"); // 必须分开写
processBuilder.redirectInput(new File("D:\\Temp\\Process\\stdin.log")); // 重定向stdin
processBuilder.redirectOutput(new File("D:\\Temp\\Process\\stdout.log")); // 重定向stdout
processBuilder.redirectError(new File("D:\\Temp\\Process\\stderr.log")); // 重定向stderr
Process process = processBuilder.start();
process.waitFor();

脚本如下:从stdin分别读取5行,写入stdout和stderr。

#!/bin/bash

for i in {1..5}
do
    read line
    echo -e "$line\n" >&1
done

for i in {1..5}
do
    read line
    echo -e "$line\n" >&2
done

相关文章

  • 使用Process执行外部命令

    1. Runtime 使用Runtime方法执行外部命令 2. ProcessBuilder 也是使用Proces...

  • [转]scala执行linux命令

    scala中执行外部命令(scala.sys.process)发表回复 目前 scala.sys.process ...

  • Gradle-执行外部命令安装APK

    # 任务 了解 Project 的 exec 使用; 练习-执行外部命令实现APK安装; # 执行外部命令 在 P...

  • Scala调用外部命令

    1. 导入sys.process包 2. 调用方式:" 外部命令 " ! 双引号内+外部命令+感叹号 e...

  • #Python#执行外部命令cmd/shell

    工作中出于对一个脚本的优化,需要使用Python执行外部命令。这篇文章也算是工作的简单总结。执行外部命令有多种方法...

  • Shell脚本(中)

    shell 数组 shell内建命令 通常来说,内建命令会比外部命令执行得更快,执行外部命令时不但会触发磁盘 I/...

  • 进程学习二

    使用go进行进程编程 1.衍生(spawn)新进程 如果你的程序需要执行外部命令,可以直接使用exec.Comma...

  • MacOS使用Process执行脚本

    一、创建脚本文件 创建.command文件,我这里命名为script.command,然后将这个文件添加到工程中。...

  • 003-golang 调用外部命令

    003-golang 调用外部命令 相关函数 exec包执行外部命令,它将os.StartProcess进行包装使...

  • Go 语言中执行外部命令的方法

    Go 语言中执行外部命令主要的方法是使用包 os/exec。 此包的详细文档见 exec package - os...

网友评论

      本文标题:使用Process执行外部命令

      本文链接:https://www.haomeiwen.com/subject/qkcynftx.html