Setting the environment for ProcessBuilder

前端 未结 6 1044
囚心锁ツ
囚心锁ツ 2020-12-06 17:14

I have a strange problem setting the Linux environment from Java (1.6); specifically the \"PATH\" variable.

In a nutshell, I have a pipeline for running native proce

相关标签:
6条回答
  • 2020-12-06 17:22

    You need to understand that environment variables are local to process contexts. A new process gets a copy of the parent's environment but each copy is independent. Changes in the parent don't affect existing children (only new ones) and changes in children don't affect the parent or new children of the parent.

    In your case, the Java process creates child process and puts a modified PATH variable into the child's context. This doesn't affect the Java process. The child process isn't a shell, so it ignores the PATH variable. The process is created directly using OS services. Those look into the context of the Java process which contains the old PATH variable unless you change the environment in the shell before you start the Java process.

    To fix your issue, you have two choices:

    1. Examine the PATH variable in Java, split it into path elements and search for the executable manually. You can then invoke ProcessBuilder with the absolute path and put the new PATH into the child, so grandchildren will have the correct path.

    2. Invoke a shell to start the child process. The shell will use it's path (which you can pass via the environment).

    The second case works like this:

    1. You create an environment with the correct PATH.
    2. You start a shell process.
    3. You pass the command to run as argument to the shell ("sh", "-c", "cmd args" or "cmd.exe", "/c", "cmd args")
    4. The shell will notice that it has to run a command
    5. It will look into it's environment (which you configured in step #1), find the modified PATH and run the correct command.

    The drawback of the second case is that you have to properly escape and/or quote the arguments for the command (args), or spaces and other special characters will cause problems.

    0 讨论(0)
  • 2020-12-06 17:30

    I think you're right. The currently executing java code will not use the environment variables you are preparing for the child process you are executing. You could create an intermediate executable or script that you can pass variables to and have it execute your program.

    0 讨论(0)
  • 2020-12-06 17:37

    One thing that is clear from the ProcessBuilder javadoc is that you can get the environment variables with the environment() method, and then modify the returned map. Any subsequent process that is launched from that ProcessBuilder instance will have your changes.

    0 讨论(0)
  • 2020-12-06 17:41

    I don't think it's a bug, I think it's a problem with your understanding of the boundaries and roles of the environment variables at play. ProcessBuilder.environment() contains environment variables that will be "process-local" to the spawned process. They are not system-wide, or logon-wide, and they don't even affect the environment in which the ProcessBuilder is running.

    The ProcessBuilder.environment() map contains process-local variables that will be seen only by the spawned process. Obviously a prerequisite to the spawned processing seeing the ProcessBuilder.environment() is successful spawning of the process, which is a point I do not think you're even getting to.

    As far as I know, it's not really possible (from Java) to modify the currently-running process' PATH, which is what I think you're expecting to happen (or to be able to do.) So I think you must point ProcessBuilder to the fully qualified path to the executable you're trying to launch (or be certain that the PATH was set up correctly before you even launch the JVM that will use the ProcessBuilder, which is what you did in your 'working' scenario of setting it in the terminal before launching your IDE).

    0 讨论(0)
  • 2020-12-06 17:42

    On Linux:

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

    In this case the PATH variable is updated as per the need and the executable is searched in new $PATH. This worked for me on Linux.

    0 讨论(0)
  • 2020-12-06 17:45

    This seems to be a real issue with java and external processes

    the following on windows 7 and java 7 (32bit)

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

    produces

    SystemRoot: C:\Windows
    Path: xbox
    

    which means the running programs environment and the subprocesses environment should contain a path variable, that has exactly the value 'xbox' (e.g. nonsense, there is no directory named xbox anywhere on my pc)

    just for protocol:

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

    gives exactly the same result.

    when I run

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

    with this process builder and environment I get

        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.
    

    this is the (german) output of

    C:\Windows\System32\convert.exe
    

    The same happens when I use

    Runtime.getRuntime().exec(new String[]{"convert.exe", "/?"});
    

    And note that my environment is so small because I replaced the native enviroment. That means the whole program has exactly those two environment variables.

    0 讨论(0)
提交回复
热议问题