java.lang.ProcessBuilder

/*  * @(#)ProcessBuilder.java    1.9 06/03/22  *  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.  *  * @author  Martin Buchholz  * @version 1.9, 06/03/22  */ package java.lang; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map;

此类用于创建操作系统进程。

每个 ProcessBuilder 实例管理一个进程属性集。start() 方法利用这些属性创建一个新的 Process 实例。start() 方法可以从同一实例重复调用,以利用相同的或相关的属性创建新的子进程。

每个进程生成器管理这些进程属性:

  • 命令 是一个字符串列表,它表示要调用的外部程序文件及其参数(如果有)。在此,表示有效的操作系统命令的字符串列表是依赖于系统的。例如,每一个总体变量,通 常都要成为此列表中的元素,但有一些操作系统,希望程序能自己标记命令行字符串——在这种系统中,Java 实现可能需要命令确切地包含这两个元素。
  • 环境 是从变量 的依赖于系统的映射。初始值是当前进程环境的一个副本(请参阅 System.getenv())。
  • 工作目录。默认值是当前进程的当前工作目录,通常根据系统属性 user.dir 来命名。
  • redirectErrorStream 属性。最初,此属性为 false,意思是子进程的标准输出和错误输出被发送给两个独立的流,这些流可以通过 Process.getInputStream()Process.getErrorStream() 方法来访问。如果将值设置为 true,标准错误将与标准输出合并。这使得关联错误消息和相应的输出变得更容易。在此情况下,合并的数据可从 Process.getInputStream() 返回的流读取,而从 Process.getErrorStream() 返回的流读取将直接到达文件尾。

修改进程构建器的属性将影响后续由该对象的 start() 方法启动的进程,但从不会影响以前启动的进程或 Java 自身的进程。

大多数错误检查由 start() 方法执行。可以修改对象的状态,但这样 start() 将会失败。例如,将命令属性设置为一个空列表将不会抛出异常,除非包含了 start()

注意,此类不是同步的。如果多个线程同时访问一个 ProcessBuilder,而其中至少一个线程从结构上修改了其中一个属性,它必须 保持外部同步。

很容易启动一个使用默认工作目录和环境的新进程:

 Process p = new ProcessBuilder("myCommand", "myArg").start();
 

下面是一个利用修改过的工作目录和环境启动进程的例子:

 ProcessBuilder pb = new ProcessBuilder("myCommand", "myArg1", "myArg2");
 Map<String, String> env = pb.environment();
 env.put("VAR1", "myValue");
 env.remove("OTHERVAR");
 env.put("VAR2", env.get("VAR1") + "suffix");
 pb.directory(new File("myDir"));
 Process p = pb.start();
 

要利用一组明确的环境变量启动进程,在添加环境变量之前,首先调用 Map.clear()

public final class ProcessBuilder {     private List<String> command;     private File directory;     private Map<String,String> environment;     private boolean redirectErrorStream;     /**      * Constructs a process builder with the specified operating      * system program and arguments.  This constructor does <i>not</i>      * make a copy of the <code>command</code> list.  Subsequent      * updates to the list will be reflected in the state of the      * process builder.  It is not checked whether      * <code>command</code> corresponds to a valid operating system      * command.</p>      *      * @param   command  The list containing the program and its arguments      *      * @throws  NullPointerException      *          If the argument is <code>null</code>      */     public ProcessBuilder(List<String> command) {     if (command == null)         throw new NullPointerException();     this.command = command;     }     /**      * Constructs a process builder with the specified operating      * system program and arguments.  This is a convenience      * constructor that sets the process builder's command to a string      * list containing the same strings as the <code>command</code>      * array, in the same order.  It is not checked whether      * <code>command</code> corresponds to a valid operating system      * command.</p>      *      * @param   command  A string array containing the program and its arguments      */     public ProcessBuilder(String... command) {     this.command = new ArrayList<String>(command.length);     for (String arg : command)         this.command.add(arg);     }     /**      * Sets this process builder's operating system program and      * arguments.  This method does <i>not</i> make a copy of the      * <code>command</code> list.  Subsequent updates to the list will      * be reflected in the state of the process builder.  It is not      * checked whether <code>command</code> corresponds to a valid      * operating system command.</p>      *      * @param   command  The list containing the program and its arguments      * @return  This process builder      *      * @throws  NullPointerException      *          If the argument is <code>null</code>      */     public ProcessBuilder command(List<String> command) {     if (command == null)         throw new NullPointerException();     this.command = command;     return this;     }     /**      * Sets this process builder's operating system program and      * arguments.  This is a convenience method that sets the command      * to a string list containing the same strings as the      * <code>command</code> array, in the same order.  It is not      * checked whether <code>command</code> corresponds to a valid      * operating system command.</p>      *      * @param   command  A string array containing the program and its arguments      * @return  This process builder      */     public ProcessBuilder command(String... command) {     this.command = new ArrayList<String>(command.length);     for (String arg : command)         this.command.add(arg);     return this;     }     /**      * Returns this process builder's operating system program and      * arguments.  The returned list is <i>not</i> a copy.  Subsequent      * updates to the list will be reflected in the state of this      * process builder.</p>      *      * @return  This process builder's program and its arguments      */     public List<String> command() {     return command;     }   返回此进程生成器环境的字符串映射视图。 无论进程生成器何时创建,都需要将环境初始化为一份当前进程环境的副本(请参阅 System.getenv())。由此对象的 start() 方法启动的后续子进程将使用这一映射作为它们的环境。

可以使用普通的 Map 操作来修改返回的对象。对于通过 start() 方法启动的子进程,这些修改是可见的。两个 ProcessBuilder 实例总是包含独立的进程环境,因此,针对返回的映射的更改从不会在任何其他 ProcessBuilder 实例或由 System.getenv 返回的值中反映出来。

如果系统不支持环境变量,将返回空映射。

返回的映射不允许空键或空值。试图插入空键或空值或者试图查询它们的存在,都将抛出 NullPointerException。试图查询非 String 类型的键或值的存在,都将抛出 ClassCastException

返回的映射的行为取决于系统。系统可能不允许修改环境变量或禁止某些变量名或变量值。出于此原因,如果不允许操作系统修改的话,试图修改映射可能失败,并抛出 UnsupportedOperationExceptionIllegalArgumentException

由于环境变量名和值的外部格式取决于系统,在它们与 Java 的 Unicode 字符串之间不可能是一对一映射。此外,映射以这种方式实现:不能由 Java 代码修改的环境变量在子进程中将有一个不可修改的本机表示形式。

返回的映射及其集合视图不能遵守 Object.equals(java.lang.Object)Object.hashCode() 方法的常规协定。

返回的映射通常在所有平台上都是区分大小写的。

如果安全管理器存在,则其 checkPermission 方法通过 RuntimePermission("getenv.*") 权限进行调用。这可能导致抛出 SecurityException

当将信息传递给 Java 子进程时,系统属性通常优先于环境变量。

    public Map<String,String> environment() {     SecurityManager security = System.getSecurityManager();     if (security != null)         security.checkPermission(new RuntimePermission("getenv.*"));     if (environment == null)         environment = ProcessEnvironment.environment();     assert environment != null;     return environment;     }     // Only for use by Runtime.exec(...envp...)     ProcessBuilder environment(String[] envp) {     assert environment == null;     if (envp != null) {         environment = ProcessEnvironment.emptyEnvironment(envp.length);         assert environment != null;         for (String envstring : envp) {         // Before 1.5, we blindly passed invalid envstrings         // to the child process.         // We would like to throw an exception, but do not,         // for compatibility with old broken code.         // Silently discard any trailing junk.         if (envstring.indexOf((int) '/u0000') != -1)             envstring = envstring.replaceFirst("/u0000.*", "");         int eqlsign =             envstring.indexOf('=', ProcessEnvironment.MIN_NAME_LENGTH);         // Silently ignore envstrings lacking the required `='.         if (eqlsign != -1)             environment.put(envstring.substring(0,eqlsign),                     envstring.substring(eqlsign+1));         }     }     return this;     }     /**      * Returns this process builder's working directory.      *      * Subprocesses subsequently started by this object's {@link      * #start()} method will use this as their working directory.      * The returned value may be <code>null</code> -- this means to use      * the working directory of the current Java process, usually the      * directory named by the system property <code>user.dir</code>,      * as the working directory of the child process.</p>      *      * @return  This process builder's working directory      */     public File directory() {     return directory;     }     /**      * Sets this process builder's working directory.      *      * Subprocesses subsequently started by this object's {@link      * #start()} method will use this as their working directory.      * The argument may be <code>null</code> -- this means to use the      * working directory of the current Java process, usually the      * directory named by the system property <code>user.dir</code>,      * as the working directory of the child process.</p>      *      * @param   directory  The new working directory      * @return  This process builder      */     public ProcessBuilder directory(File directory) {     this.directory = directory;     return this;     }     /**      * Tells whether this process builder merges standard error and      * standard output.      *      * <p>If this property is <code>true</code>, then any error output      * generated by subprocesses subsequently started by this object's      * {@link #start()} method will be merged with the standard      * output, so that both can be read using the      * {@link Process#getInputStream()} method.  This makes it easier      * to correlate error messages with the corresponding output.      * The initial value is <code>false</code>.</p>      *      * @return  This process builder's <code>redirectErrorStream</code> property      */     public boolean redirectErrorStream() {     return redirectErrorStream;     }     /**      * Sets this process builder's <code>redirectErrorStream</code> property.      *      * <p>If this property is <code>true</code>, then any error output      * generated by subprocesses subsequently started by this object's      * {@link #start()} method will be merged with the standard      * output, so that both can be read using the      * {@link Process#getInputStream()} method.  This makes it easier      * to correlate error messages with the corresponding output.      * The initial value is <code>false</code>.</p>      *      * @param   redirectErrorStream  The new property value      * @return  This process builder      */     public ProcessBuilder redirectErrorStream(boolean redirectErrorStream) {     this.redirectErrorStream = redirectErrorStream;     return this;     } 使用此进程生成器的属性启动一个新进程。

directory() 指定的工作目录中,利用 environment() 指定的进程环境,新进程将调用由 command() 给出的命令和参数。

此方法检查命令是否为一条有效的操作系统命令。哪条命令是有效的呢?这取决于系统,但至少命令必须是非空字符串的非空列表。

如果有安全管理器,则用此对象的 command 数组的首个元素作为其参数来调用安全管理器的 checkExec 方法。这可能导致抛出 SecurityException

启动操作系统进程的方式完全取决于系统。其中有很多方面会导致错误:

  • 未找到操作系统程序文件。
  • 对程序文件的访问被拒绝。
  • 工作目录不存在。

在这些情况中将会抛出一个异常。该异常的具体本质取决于系统,但它总是 IOException 的一个子类。

针对此进程生成器的后续修改将不会影响返回的 Process

    public Process start() throws IOException {     // Must convert to array first -- a malicious user-supplied     // list might try to circumvent the security check.     String[] cmdarray = command.toArray(new String[command.size()]);     for (String arg : cmdarray)         if (arg == null)         throw new NullPointerException();     // Throws IndexOutOfBoundsException if command is empty     String prog = cmdarray[0];     SecurityManager security = System.getSecurityManager();     if (security != null)         security.checkExec(prog);     String dir = directory == null ? null : directory.toString();     try {         return ProcessImpl.start(cmdarray,                      environment,                      dir,                      redirectErrorStream);     } catch (IOException e) {         // It's much easier for us to create a high-quality error         // message than the low-level C code which found the problem.         throw new IOException(         "Cannot run program /"" + prog + "/""         + (dir == null ? "" : " (in directory /"" + dir + "/")")         + ": " + e.getMessage(),         e);     }     } }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值