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?
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 effect 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 theProcessBuilder.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).
This seems to be a real issue with java and external processes
the following on windows 7 and java 7 (32bit)
produces
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:
gives exactly the same result.
when I run
with this process builder and environment I get
this is the (german) output of
The same happens when I use
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.
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.
On Linux:
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.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.
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 thePATH
variable. The process is created directly using OS services. Those look into the context of the Java process which contains the oldPATH
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 invokeProcessBuilder
with the absolute path and put the newPATH
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.