【问题标题】:Setting the environment for ProcessBuilder设置 ProcessBuilder 的环境
【发布时间】:2012-04-19 14:14:21
【问题描述】:

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

简而言之,我有一个用于运行本机进程的管道,它使用java.lang.ProcessBuilder。用户可以选择通过名为environmentHashMap 设置环境变量:

ProcessBuilder pb = new ProcessBuilder(args);
Map<String, String> env = pb.environment();
if (environment != null)
   env.putAll(environment);
Process process = pb.start();

env 变量设置正确,如果我将它转储到控制台,PATH 变量的值正确。但是,运行该进程会导致抛出Exception

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

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

那么肯定是ProcessBuilder 没有使用我为其设置的环境,而是使用当前的系统环境。

我在网上找不到任何令人满意的答案。也许这是一个特定于操作系统的问题?或者我还缺少什么?

【问题讨论】:

  • 也许尝试 sysout PATH 变量并将其发布在这里,也许我们可以发现一些东西..
  • 在 Windows 中要注意的另一件事是,虽然环境变量名称的大小写是 “通常不重要”(引用 System.getenvjavadoc),因此您可以互换调用System.getenv("path")System.getenv("PATH") 并得到类似的结果,对于ProcessBuilder 的环境字段则不是这样,因为Map 是直接访问而不是通过特定的getter,因此大小写的任何差异都会导致错误。 .. 提示:如果您确实想要在 Windows 中获取 ProcessBuilder 的路径,请使用 environment.get("Path")
  • @PeterT 你给的提示说在windows上使用路径太棒了!!!由于 ProcessBuilder 中的这个问题,我一起度过了几个星期!多谢 !!! :) :)
  • @Destructor 很高兴为您提供帮助...我不记得它挫败了我多长时间:不是几周,但足以令人沮丧:)

标签: java environment-variables processbuilder


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

    ProcessBuilder.environment() 映射包含进程局部变量,这些变量可以被生成的进程看到。显然,看到ProcessBuilder.environment() 的生成处理的先决条件是成功生成该进程,我认为您甚至没有达到这一点。

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

    【讨论】:

    • 谢谢,这很有帮助。假设我指定了目标进程的完整路径,这是否意味着如果它自己调用其他进程,它将使用ProcessBuilder 的进程本地环境变量?这对我来说是一个合适的解决方案。
    • 我不确定我是否完全理解(尤其是关于“调用其他进程”),但我很确定答案是肯定的。
    • 我明白你的意思。你能指出我指定相同的文档吗?
    【解决方案3】:

    在 Linux 上:

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

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

    【讨论】:

    • 或运行一个 sh 文件并对其进行编辑以在该 sh 脚本中执行之前拥有所需的变量
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      这似乎是 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的目录)

      仅用于协议:

      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", "/?"});
      

      请注意,我的环境是如此之小,因为我替换了原生环境。这意味着整个程序正好有这两个环境变量。

      【讨论】:

        【解决方案6】:

        您需要了解环境变量是进程上下文的本地变量。一个新进程获取父环境的副本,但每个副本都是独立的。父项的更改不会影响现有的子项(仅新的子项),子项的更改不会影响父项的父项或新子项。

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

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

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

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

        第二种情况是这样的:

        1. 您使用正确的PATH 创建环境。
        2. 您启动了一个 shell 进程。
        3. 您将要运行的命令作为参数传递给 shell("sh", "-c", "cmd args""cmd.exe", "/c", "cmd args"
        4. shell 会注意到它必须运行一个命令
        5. 它将查看它的环境(您在步骤 #1 中配置),找到修改后的 PATH 并运行正确的命令。

        第二种情况的缺点是您必须正确转义和/或引用命令的参数 (args),否则空格和其他特殊字符会导致问题。

        【讨论】:

          猜你喜欢
          • 2011-11-13
          • 2015-01-15
          • 2018-07-23
          • 2014-06-02
          • 1970-01-01
          • 1970-01-01
          • 2015-08-14
          • 1970-01-01
          相关资源
          最近更新 更多