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 processes, which uses java.lang.ProcessBuilder
. The user can optionally set the environment variables via a HashMap
named environment
:
ProcessBuilder pb = new ProcessBuilder(args);
Map<String, String> env = pb.environment();
if (environment != null)
env.putAll(environment);
Process process = pb.start();
The env
variable gets set properly, if I dump it to the console, with a correct value for the PATH variable. However, running the process results in a thrown Exception
:
java.io.IOException: error=2, No such file or directory
The same process runs fine with identical environment variables in the terminal shell. To test this, I ran Eclipse AFTER setting the environment in the terminal. In this case the ProcessBuilder
process runs correctly.
So what must be happening is that the ProcessBuilder
is not using the environment I set for it, but the current System environment instead.
I can't find any satisfactory answers to this problem online. Perhaps this is an OS-specific issue? Or something else I'm missing?
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:
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.
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:
PATH
."sh", "-c", "cmd args"
or "cmd.exe", "/c", "cmd args"
)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.