`
berdy
  • 浏览: 513643 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

调用Runtime.exec()的一些陷阱

阅读更多
    Runtime 封装着java程序的运行时环境。通过Runtime实例,java应用能够与其运行的环境连接。Runtime在jvm中保持一个单例,所以不能通过Runtime类的构造函数。只能通过Runtime.getRuntime()来获的当前Runtime的一个实例。获得Runtime实例后,就可以通过Runtime的exec()方法在当前jvm进程外启动其他进程了。很常见的一个应用就是,启动浏览器进程来显示一个程序的帮助页面。

在Runtime类中存在四个exec()重载方法.
public Process exec(String command);
public Process exec(String [] cmdArray);
public Process exec(String command, String [] envp);
public Process exec(String [] cmdArray, String [] envp);

主要参数是要启动进程的名称,以及启动该进程时需要的参数。然后是一些环境相关的属性。envp是已name=value,
形式传入的。具体查看下源码便一目了然了。

    通常,启动另外一个进程后,需要获取另外一个进程的执行结果,然后根据结果执行后续的流程。要获取外部进程的运行结果,可以调用Process的exitValue() 方法。下面代码中启动一个java编译器进程。
try {
	Runtime rt = Runtime.getRuntime();
	Process proc = rt.exec("javac");
	int exitVal = proc.exitValue();
	System.out.println("Process exitValue: " + exitVal);
} catch (Throwable t) {
	t.printStackTrace();
}

不幸的是,你将看到下面的结果:
java.lang.IllegalThreadStateException: process has not exited
at java.lang.ProcessImpl.exitValue(Native Method)
at com.test.runtime.Test.BadExecJavac(Test.java:13)
at com.test.runtime.Test.main(Test.java:5)

原因是exitValue()方法并不会等待外部进程结束。如果外部进程还未结束,exitValue()将会抛出IllegalThreadStateException。解决办法就是调用Process的waitfor()方法。waitfor()方法会挂起当前线程,一直等到外部进程结束。当然使用exitValue()或者waitfor()完全取决你的需求。可以设个boolean标志,来确定使用哪个。运行下面的代码:
try {
	Runtime rt = Runtime.getRuntime();
	Process proc = rt.exec("javac");
	int exitVal = proc.waitFor();
	System.out.println("Process exitValue: " + exitVal);
} catch (Throwable t) {
	t.printStackTrace();
}

发现程序被阻塞了,什么原因呢?JDK's Javadoc文档解释说:
Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock.
翻译:
一些平台只为标准输入输出提供有限的缓存。错误的写子进程的输入流或者错误的都子进程的输出流都有可能造成子进程的阻塞,甚至是死锁。

解决上面问题的办法就是程序中将子进程的输出流和错误流都输出到标准输出中。
try {
	Runtime rt = Runtime.getRuntime();
	Process proc = rt.exec("javac");
	InputStream stderr = proc.getErrorStream();
	InputStreamReader isr = new InputStreamReader(stderr);
	BufferedReader br = new BufferedReader(isr);
	String line = null;
	System.out.println("<ERROR>");
	while ((line = br.readLine()) != null)
		System.out.println(line);
	System.out.println("</ERROR>");
	int exitVal = proc.waitFor();
	System.out.println("Process exitValue: " + exitVal);
} catch (Throwable t) {
	t.printStackTrace();
}

上面的代码中仅仅是输出了错误流,并没有输出子进程的输出流。在程序中最好是能将子进程的错误流和输出流都能输出并清空。

在windows系统中,很多人会利用Runtime.exec()来调用不可执行的命令。例如dir和copy;
try {
	Runtime rt = Runtime.getRuntime();
	Process proc = rt.exec("dir");
	InputStream stdin = proc.getInputStream();
	InputStreamReader isr = new InputStreamReader(stdin);
	BufferedReader br = new BufferedReader(isr);
	String line = null;
	System.out.println("<OUTPUT>");
	while ((line = br.readLine()) != null)
		System.out.println(line);
	System.out.println("</OUTPUT>");
	int exitVal = proc.waitFor();
	System.out.println("Process exitValue: " + exitVal);
} catch (Throwable t) {
	t.printStackTrace();
}

运行上面的代码,将会得到一个错误代码为2的错误。在win32系统中,error=2表示文件未找到。也就是不存在dir.exe和copy.exe。这是因为dir命令式windows中命令行解析器的一部分,并不是单独的一个可执行的命令。要运行上面的命令,得先启动windows下的命令行解析器command.com或者cmd.exe,这个取决于windows的系统的版本。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

class StreamGobbler extends Thread {
	InputStream is;
	String type;

	StreamGobbler(InputStream is, String type) {
		this.is = is;
		this.type = type;
	}

	public void run() {
		try {
			InputStreamReader isr = new InputStreamReader(is);
			BufferedReader br = new BufferedReader(isr);
			String line = null;
			while ((line = br.readLine()) != null)
				System.out.println(type + ">" + line);
		} catch (IOException ioe) {
			ioe.printStackTrace();
		}
	}
}

public class GoodWindowsExec {
	public static void main(String args[]) {
		if (args.length < 1) {
			System.out.println("USAGE: java GoodWindowsExec <cmd>");
			System.exit(1);
		}

		try {
			String osName = System.getProperty("os.name");
			String[] cmd = new String[3];
			if (osName.equals("Windows NT")) {
				cmd[0] = "cmd.exe";
				cmd[1] = "/C";
				cmd[2] = args[0];
			} else if (osName.equals("Windows 95")) {
				cmd[0] = "command.com";
				cmd[1] = "/C";
				cmd[2] = args[0];
			}

			Runtime rt = Runtime.getRuntime();
			System.out.println("Execing " + cmd[0] + " " + cmd[1] + " " + cmd[2]);
			Process proc = rt.exec(cmd);

			StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");

			StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT");

			errorGobbler.start();
			outputGobbler.start();

			int exitVal = proc.waitFor();
			System.out.println("ExitValue: " + exitVal);
		} catch (Throwable t) {
			t.printStackTrace();
		}
	}
}

另外,Runtime.exec()并不是命令解析器,这是启动某个进程。并不能执行一些命令行的命令。下面是一个常见的错误:
try {
	Runtime rt = Runtime.getRuntime();
	Process proc = rt.exec("java jecho 'Hello World' > test.txt");

	StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");

	StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT");

	errorGobbler.start();
	outputGobbler.start();

	int exitVal = proc.waitFor();
	System.out.println("ExitValue: " + exitVal);
} catch (Throwable t) {
	t.printStackTrace();
}

上面的代码希望像DOS系统中一样将命令的执行结果输出到文件中去。但是Runtime.exec()并不是命令行解析器。要想重定向输出流,必须在程序中编码实现。
import java.util.*;
import java.io.*;

class StreamGobbler extends Thread {
	InputStream is;
	String type;
	OutputStream os;

	StreamGobbler(InputStream is, String type) {
		this(is, type, null);
	}

	StreamGobbler(InputStream is, String type, OutputStream redirect) {
		this.is = is;
		this.type = type;
		this.os = redirect;
	}

	public void run() {
		try {
			PrintWriter pw = null;
			if (os != null)
				pw = new PrintWriter(os);
			InputStreamReader isr = new InputStreamReader(is);
			BufferedReader br = new BufferedReader(isr);
			String line = null;
			while ((line = br.readLine()) != null) {
				if (pw != null)
					pw.println(line);
				System.out.println(type + ">" + line);
			}
			if (pw != null)
				pw.flush();
		} catch (IOException ioe) {
			ioe.printStackTrace();
		}
	}
}

public class GoodWinRedirect {
	public static void main(String args[]) {
		if (args.length < 1) {
			System.out.println("USAGE java GoodWinRedirect <outputfile>");
			System.exit(1);
		}

		try {
			FileOutputStream fos = new FileOutputStream(args[0]);
			Runtime rt = Runtime.getRuntime();
			Process proc = rt.exec("java jecho 'Hello World'");

			StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");
			StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT", fos);

			errorGobbler.start();
			outputGobbler.start();

			int exitVal = proc.waitFor();
			System.out.println("ExitValue: " + exitVal);
			fos.flush();
			fos.close();
		} catch (Throwable t) {
			t.printStackTrace();
		}
	}
}

参考资料:
When Runtime.exec() won't
3
0
分享到:
评论
3 楼 di1984HIT 2014-07-30  
用cmd /c java -jar a.jar 1>a.log 2>&1应该可以。
2 楼 wancaibida 2012-05-25  
先处理getErrorStream就不会卡在那儿了
1 楼 wfeng007 2011-10-26  
不错不错
很多java写的本地脚本接口都需要这个....

相关推荐

    Java使用Runtime.exec()给Windows命令提示符做了个外壳,真的很山寨!

    在Java编程中,`Runtime.exec()`方法是一个非常实用的功能,它允许我们执行操作系统级别的命令。这篇博客"Java使用Runtime.exec()给Windows命令提示符做了个外壳,真的很山寨!"探讨了如何利用`Runtime.exec()`来...

    System.Runtime.InteropServices.WindowsRuntime 蓝牙相关开发库

    C#开发者可以通过System.Runtime.InteropServices.WindowsRuntime调用相关API来查找BLE设备的服务和特性,进行数据交换。 7. **蓝牙广告和广播**:Windows Runtime还支持蓝牙设备的广播和扫描功能,C#开发者可以...

    Esri.ArcGISRuntime.WPF.100.4.0.nupkg

    初始化代码中调用以下代码,可激活所有模块 ArcGISRuntimeEnvironment.Initialize(); string licenseKey = "runtimeadvanced,1000,rud12345678,none,12345678"; string[] extensions = { "runtimeanalysis,1000,...

    java中两种方式调用其他.exe可执行程序

    Runtime.getRuntime().exec("cmd /k start Msconfig.exe"); ``` 在上面的代码中,我们使用 `Runtime` 类的 `getRuntime` 方法获取当前进程,然后使用 `exec` 方法执行一个外部命令,打开 cmd 命令行并启动 Msconfig....

    Java调用应用程序和Dos中的命令

    下面是一个简单的示例,展示如何使用`Runtime.exec()`来调用一个外部应用程序(假设为`notepad.exe`): ```java import java.io.IOException; public class CallApplication { public static void main(String[]...

    java调用播放器

    ### Java调用播放器知识点详解 #### 一、概述 ...通过上述内容,我们可以了解到如何在Java中使用`Runtime.exec()`方法来调用播放器以及执行其他系统命令。这对于开发具有多媒体功能的应用程序非常有用。

    java调用windows命令

    如果遇到无法删除的问题,可以尝试使用`Runtime.exec()`调用`del`命令强制删除文件。 在实际编程中,处理这些细节能够确保Java程序在Windows环境中正确地调用命令行操作。同时,为了提高代码的可维护性和可读性,...

    c# / VB 调用refprop.dll

    在C#或VB.NET中调用`refprop.dll`,你需要利用.NET Framework的P/Invoke(Platform Invoke)特性,它允许.NET代码调用非托管(如C或C++编写的)DLL中的函数。以下是一个简单的C#示例: ```csharp using System; ...

    Java调用Python的jar包

    首先,我们需要了解Java如何调用外部程序,这通常通过`java.lang.ProcessBuilder`类或`Runtime.getRuntime().exec()`方法来实现。这两个方法允许Java启动一个新的进程,并执行操作系统命令。因此,我们可以在Java中...

    NIVISA1700runtime.rar

    安装过程中,系统会自动检测兼容性,安装必要的驱动和组件,并将运行时环境添加到系统的路径中,以便于应用程序调用。 4. **适用平台** NI VISA运行时环境适用于Windows操作系统,支持多种版本,包括Windows 7、8...

    android系统中调用shell脚本

    2. **使用Runtime.exec()的变体**:如果需要传递参数或者捕获脚本输出,可以使用`Runtime.exec(String[] cmdarray)`,这样可以更好地控制命令行参数。 3. **使用`java.lang.ProcessBuilder`**:此类提供了更灵活的...

    Java调用Shell命令的方法

    总之,Java通过`Runtime.exec()`方法调用Shell命令,实现了与操作系统交互的能力。这种方式在处理系统级任务时非常有用,尤其是在需要执行复杂操作或者利用已有系统工具时。然而,也应当谨慎使用,以防止潜在的安全...

    java系统命令调用

    本文将详细介绍如何使用Java Runtime类中的`getRuntime().exec()`方法来调用系统命令,并提供一些实际应用场景的例子。 ### Java Runtime.getRuntime().exec() `java.lang.Runtime`类提供了运行时系统的表示形式,...

    sql INSERT...EXEC

    在一个存储过程中调用另一个存储过程的结果集数据(同一个服务器,结果集只能是第一个)

    ffmpeg.zip

    在Java开发中,我们可以利用Runtime.exec()方法来调用FFmpeg的可执行文件,从而实现对视频文件的操作。 FFmpeg的核心组件包括ffmpeg、ffprobe和ffplay。其中,ffmpeg是主要的命令行工具,用于执行各种媒体处理任务...

    ios-Runtime.zip

    在这个“ios-Runtime.zip”压缩包中,我们很可能是得到了关于iOS Runtime系统的一些资料或者示例代码。 Runtime系统的核心概念包括以下几个方面: 1. **类(Class)**:在Objective-C中,每个类都是一个对象,它定义...

    VB调用winrar.dll进行rar文件的解压缩

    虽然可以借助第三方工具如WinRAR来实现RAR文件的解压缩,但直接通过编程调用WinRAR的动态链接库(DLL)可以提供更稳定且可控的解决方案。本篇将详细讲解如何使用VB调用winrar.dll进行RAR文件的解压缩。 首先,你...

    java调用python脚本

    但在这里,我们将更常见地使用Java的`ProcessBuilder`或`Runtime.exec()`方法来执行外部进程,也就是我们的Python脚本。 1. **使用ProcessBuilder**: `ProcessBuilder`类允许我们创建和控制外部进程。以下是一个...

    onnxruntime v1.14.0 win7操作系统64位dll

    onnxruntime1.15.0版本以后不支持win...版本:onnxruntime1.14.0操作系统:win7编译器:VS2019动态链接库为64位,release版本,已经win7 64位操作系统下VS2019 C++调用dll联调测试通过,现在共享出来给有需要的小伙伴。

Global site tag (gtag.js) - Google Analytics