解决使用ProcessBuilder踩到的坑及注意事项

网友投稿 277 2023-01-09

解决使用ProcessBuilder踩到的坑及注意事项

使用ProcessBuilder踩到的坑

最近使用ProcessBuilder执行命令,命令内容正确,但始终报错命令实行失败,是因为不熟悉ProcessBuilder用法踩到了坑,记录一下。

先看一下我模拟出来的错误

要执行的命令:cp -rf /tmp/monkey/a.log /home/monkey/ 简单的cp命令拷贝一个文件,却报错说文件不存在。确认过文件确实存在该目录下。

查看jdk 中,我使用的ProcessBuilder(***) 源码实现如下,并不是一个单独的字符串String形式,而是支持多个字符串,同时还有List集合方式。

于是想到会不会是ProcessBuilderbuilder不支持包含空格的命令。

动手写了下面的代码进行测试

public class ProcessBuilderDemo {

/**

* 测试processBuilder执行cp命令

* cp /tmp/monkey/a.log /home/monkey/

* 源路径 args[1]: /tmp/monkey/a.log

* 目标路径 args[2]: /home/monkey/

* 方法名 args[3]

* @param args

*/

public static void main(String[] args) {

String src = args[0];

String tag = args[1];

String method = args.length == 3 ? args[2] : null;

if (method != null && method.equals("string")) {

cmdIsString(src, tag);

} else {

cmdIsListOrArray(src, tag);

}

}

/**

* 执行命令,命令用拼接成一个字符串形式(会包含空格)

* @param src 源路径

* @param tag 目标路径

*/

private static void cmdIsString(String src, String tag) {

String cmd = "cp";

cmd = cmd + " -rf" + " " + src + " " + tag;

System.out.println("command is: " + cmd);

ProcessBuilder builder = new ProcessBuilder(cmd);

try {

Process process = builder.start();

process.waitFor();

} catch (Exception e) {

e.printStackTrace();

System.out.println(e.toString());

}

}

/**

* 执行命令,命令各个部分拼接成一个数组或者ArrayList集合

* 该方法采用数组实现

* @param src 源路径

* @param tag 目标路径

*/

private static void cmdIsListOrArray(String src, String tag) {

String cmd = "cp";

// 命令的各个部分组成一个字符串数组,用该数组创建ProcessBuilder对象

String[] cmds = new String[] {cmd, "-rf", src, tag};

ProcessBuilder builder = new ProcessBuilder(cmds);

try {

Process process = builder.start();

process.waitFor();

} catch (Exception e) {

e.printStackTrace();

System.out.println(e.toString());

}

}

}

果然如我所猜想的一样:包含有空格的命令执行会报错。

以下是cmdIsListOrArray方法,将命令的内容组成字符串的形式执行的结果,而文章第一张图则是直接当做一条完整命令的执行结果。

至于为什么不能好有空格暂时未做深入了解,有带佬可以释疑吗?难道一条完整的命令当做一个字符串它不香嘛?

while(true) {

伸手党;

}

使用ProcessBuilder执行本地程序的注意事项

错误代码

public static void main(String[] args) throws IOException, InterruptedException {

ProcessBuilder processBuilder = new ProcessBuilder("java","-version");

Process process = processBuilder.start();

InputStream inputStream = process.getInputStream();

xxx(inputStream);

}

private static void xxx(InputStream inputStream) throws IOException {

BufferedReader input = new BufferedReader(new InputStreamReader(inputStream));

String ss=null;

while ((ss=input.readLine())!=null){

System.out.println(ss);

}

}

使用ProcessBuilder类带参执行命令容易出现的两个坑

1、执行后没有任何反映

原因为通过ProcessBuilder运行的参数还没有执行完毕程序就退出了。

通过if(process.isAlive()){process.waitFor();}可以规避此问题,但是需要注意waitFor时程序时阻塞的,如果是持续运行的web项目可以通过开启子线程来执行ProcessBuilder

2、执行后没有任何输出

最恶心的地方,除了getInputStream外还有一个getErrorStream也可以获取数据,而且一般执行的程序数据都会输出在getErrorStream中,所以getInputStream无法获取到数据

处理后的代码

public static void main(String[] args) throws IOException, InterruptedException {

ProcessBuilder processBuilder = new ProcessBuilder("java","-version");

processBuilder.redirectErrorStream(true);//将错误流中的数据合并到输入流

Process process = processBuilder.start();

if(process.isAlive()){

process.waitFor();

}

// InputStream errorStream = process.getErrorStream();

InputStream inputStream = process.getInputStream();

// xxx(errorStream);

xxx(inputStream);

}

private static void xxx(InputStream inputStream) throws IOException {

BufferedReader input = new BufferedReader(new InputStreamReader(inputStream));

String ss=null;

while ((ss=input.readLine())!=null){

System.out.println(ss);

}

}

后续发现新的问题,当某个软件会持续向流中写数据,这时流中数据没有被读取完毕(流中存在数据【测试发现流中存在数据并不是一定会阻塞】),会导致waitFor一直陷入阻塞

上述问题处理后的代码(正确使用ProcessBuilder的代码)

public static void main(String[] args) throws IOException, InterruptedException {

ProcessBuilder processBuilder = new ProcessBuilder("java","-version");

processBuilder.redirectErrorStream(true);

Process process = processBuilder.start();

// 通过标准输入流来拿到正常和错误的信息

InputStream inputStream = process.getInputStream();

BufferedReader input = new BufferedReader(new InputStreamReader(inputStream));

String ss=null;

while ((ss=input.readLine())!=null){

System.out.println(ss);

}

process.waitFor();

}

复现错误:

1、某个软件持续向流中写数据时,如果流中数据未被读取完毕waitFor一直陷入等待

public static void main(String[] args) throws IOException, InterruptedException {

ProcessBuilder processBuilder = new ProcessBuilder();

List meta = new ArrayList();

meta.add("ffmpeg");

meta.add("-i");

meta.add("C:/Users/Lenovo/Desktop/20200801134820261.mp3");

meta.add("-af");

meta.add("silencedetect=n=-1dB:d=0.5");

meta.add("-f");

meta.add("null");

meta.add("-");

processBuilder.command(meta);

processBuilder.redirectErrorStream(true);

Process process = processBuilder.start();

InputStream inputStream = process.getInputStream();

// BufferedReader input = new BufferedReader(new InputStreamReader(inputStream));

// String ss=null;

// while ((ss=input.readLine())!=null){

// System.out.println(ss);

// }

process.waitFor();

System.out.println("一直阻塞无法执行到这一步");

}

2、通过下面代码证明上面的观点:当将流中数据读取完毕后waitFor不会阻塞,可执行下一步

public static void main(String[] args) throws IOException, InterruptedException {

ProcessBuilder processBuilder = new ProcessBuilder();

List meta = new ArrayList();

meta.add("ffmpeg");

meta.add("-i");

meta.add("C:/Users/Lenovo/Desktop/20200801134820261.mp3");

meta.add("-af");

meta.add("silencedetect=n=-1dB:d=0.5");

meta.add("-f");

meta.add("null");

meta.add("-");

processBuilder.command(meta);

processBuilder.redirectErrorStream(true);

Process process = processBuilder.start();

InputStream inputStream = process.getInputStream();

BufferedReader input = new BufferedReader(new InputStreamReader(inputStream));

String ss=null;

while ((ss=input.readLine())!=null){

System.out.println(ss);

}

process.waitFor();

System.out.println("正常输出");

}

3、与上面观点产生矛盾的代码:下面代码中的流中仍然存在数据,但是waitFor并没有陷入阻塞,推测原因可能是由于ipconfig与ffmpeg不同,不存在 持续向流中写数据情况,因此waitFor可以正常结束阻塞

public static void main(String[] args) throws IOException, InterruptedException {

ProcessBuilder processBuilder = new ProcessBuilder();

List meta = new ArrayList();

meta.add("ipconfig");

processBuilder.command(meta);

processBuilder.redirectErrorStream(true);

Process process = processBuilder.start();

InputStream inputStream = process.getInputStream();

// BufferedReader input = new BufferedReader(new InputStreamReader(inputStream));

// String ss=null;

// while ((ss=input.readLine())!=null){

// System.out.println(ss);

// }

process.waitFor();

System.out.println("正常输出");

}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:跨越快递物流查询单号是什么(跨越单号查询快递单号查询)
下一篇:解决Process.getInputStream()阻塞的问题
相关文章

 发表评论

暂时没有评论,来抢沙发吧~