为ProcessBuilder设置环境

时间:2012-04-05 20:17:24

标签: java environment-variables processbuilder

我从Java(1.6)设置Linux环境有一个奇怪的问题;特别是“PATH”变量。

简而言之,我有一个用于运行本机进程的管道,它使用java.lang.ProcessBuilder。用户可以选择通过名为HashMap的{​​{1}}

来设置环境变量
environment

如果我将ProcessBuilder pb = new ProcessBuilder(args); Map<String, String> env = pb.environment(); if (environment != null) env.putAll(environment); Process process = pb.start(); 变量转储到控制台,并且PATH变量的值正确,则env变量设置正确。但是,运行该进程会导致抛出Exception

java.io.IOException: error=2, No such file or directory

在终端shell中使用相同的环境变量运行相同的进程。为了测试这个,我在终端后设置环境后运行Eclipse。在这种情况下,ProcessBuilder进程正确运行。

所以必须发生的是ProcessBuilder没有使用我为它设置的环境,而是使用当前的系统环境。

我在网上找不到任何满意的答案。也许这是特定于操作系统的问题?或者其他我想念的东西?

6 个答案:

答案 0 :(得分:13)

我认为这不是一个错误,我认为你理解环境变量的边界和角色是一个问题。 ProcessBuilder.environment()包含对生成的进程“进程本地”的环境变量。它们不是系统范围的,也不是登录范围的,它们甚至不会影响运行ProcessBuilder的环境。

ProcessBuilder.environment()地图包含由衍生流程的流程本地变量。显然,生成处理看到ProcessBuilder.environment()的先决条件是成功生成过程,这是我认为你甚至没有达到的一点。

据我所知,(从Java)修改当前正在运行的进程'PATH是不可能的,这是我认为你期望发生的(或者能够做到的)。所以我认为您必须将ProcessBuilder指向您尝试启动的可执行文件的完全限定路径(或者确保在您启动将使用ProcessBuilder的JVM之前已正确设置PATH,这是您在'工作中所做的' '在启动IDE之前在终端中设置它的方案。)

答案 1 :(得分:7)

在Linux上:

String path = System.getenv("HOME");

ProcessBuilder pb = new ProcessBuilder("/bin/bash","-c","export PATH=" +
    "PATH-TO-ADD" + ":" + path + " && exec");

在这种情况下,PATH变量根据需要进行更新,并在新$PATH中搜索可执行文件。这在Linux上适用于我。

答案 2 :(得分:6)

您需要了解环境变量是过程上下文的本地变量。新进程获取父级环境的副本,但每个副本都是独立的。父母的变化不会影响现有的孩子(只有新孩子),孩子的变化不会影响父母的父母或新孩子

在您的情况下,Java进程创建子进程并将修改后的PATH变量放入子进程的上下文中。这不会影响Java进程。子进程不是shell,因此忽略PATH变量。该过程直接使用OS服务创建。这些内容将查看包含旧PATH变量的Java进程的上下文,除非您在启动Java进程之前更改shell 中的环境。

要解决您的问题,您有两种选择:

  1. 检查Java中的PATH变量,将其拆分为路径元素并手动搜索可执行文件。然后,您可以使用绝对路径调用ProcessBuilder,并将新的PATH放入子项中,这样孙子将拥有正确的路径。

  2. 调用shell以启动子进程。 shell将使用它的路径(您可以通过环境传递)。

  3. 第二种情况是这样的:

    1. 您使用正确的PATH
    2. 创建环境
    3. 您启动了一个shell进程。
    4. 您将命令作为参数运行到shell("sh", "-c", "cmd args""cmd.exe", "/c", "cmd args"
    5. shell会注意到它必须运行命令
    6. 它将查看它的环境(您在步骤#1中配置),找到修改后的PATH并运行正确的命令。
    7. 第二种情况的缺点是你必须正确地转义和/或引用命令的参数(args),否则空格和其他特殊字符会引起问题。

答案 3 :(得分:1)

ProcessBuilder javadoc清楚的一点是,您可以使用environment()方法获取环境变量,然后修改返回的地图。从该ProcessBuilder实例启动的任何后续进程都将进行更改。

答案 4 :(得分:0)

我认为你是对的。当前正在执行的java代码将不使用您正在为正在执行的子进程准备的环境变量。您可以创建一个中间可执行文件或脚本,您可以将变量传递给它并让它执行您的程序。

答案 5 :(得分:0)

这似乎是java和外部进程的真正问题

以下是Windows 7和java 7(32位)

ProcessBuilder b = new ProcessBuilder();
Map<String, String> env = b.environment();
for (String key : env.keySet())
     System.out.println(key + ": " + env.get(key));

产生

SystemRoot: C:\Windows
Path: xbox

这意味着正在运行的程序环境和子进程环境应该包含一个路径变量,它具有值'xbox'(例如废话,我的电脑上没有名为xbox的目录)< / p>

仅适用于协议:

Map<String, String> env = System.getenv();
    for (String key : env.keySet())
        System.out.println(key + ": " + env.get(key));

给出完全相同的结果。

当我跑

b.command("convert.exe", "/?").inheritIO().start();

使用此流程构建器和环境

    Konvertiert FAT-Volumes in NTFS.

CONVERT Volume /FS:NTFS [/V] [/CvtArea:Dateiname] [/NoSecurity] [/X]

  Volume      Bestimmt den Laufwerkbuchstaben (gefolgt von einem Doppelpunkt),
              den Bereitstellungspunkt oder das Volume.
  /FS:NTFS    Bestimmt das in NTFS zu konvertierende Volume.
  /V          Legt fest, dass CONVERT im ausf�hrlichen Modus ausgef�hrt wird.
  /CvtArea:Dateiname
              Bestimmt die zusammenh�ngende Datei im Stammverzeichnis, die als
              Platzhalter f�r NTFS-Systemdateien dienen soll.
  /NoSecurity Bestimmt die Sicherheitseinstellungen f�r konvertierte Dateien
              und Verzeichnisse, die f�r jeden Benutzer zug�nglich sind.
  /X          Erzwingt ggf. das Aufheben der Bereitstellung.
              Alle ge�ffneten Handles auf das Volume sind in diesem Fall 
              ung�ltig.

这是

的(德国)输出
C:\Windows\System32\convert.exe

使用

时会发生同样的情况
Runtime.getRuntime().exec(new String[]{"convert.exe", "/?"});

请注意我的环境太小了,因为我取代了原生环境。这意味着整个程序正好具有这两个环境变量。