Reputation: 1612
I need to run a native process from Java inside a symlink directory. Let's have following directory structure:
guido@Firefly:~/work$ tree
.
└── path
└── to
└── symlink -> /home/guido/work
3 directories, 0 files
I'm starting the Java application from the ~/work
directory and want to run the native process in the ~/work/path/to/symlink
directory.
However, if I use following Java code, the symlink working directory resolves to the real path. Instead, I would like to run the command in the absolute path.
(Please mind, that pwd
command is just for illustration and should be replaced with the "real" one (e.g. go build
in my case)).
File baseDir = new File("/home/guido/work");
File link = new File(baseDir, "path/to/symlink");
Files.createSymbolicLink(link.toPath(), baseDir.toPath());
Process process = new ProcessBuilder()
.command("pwd")
.directory(link)
.start();
String output = getOutput(process);
System.out.println(output); // Prints: /home/guido/work
I was able to meet my needs with following workaround, but it looks stupid to start a shell just to be able to trigger a simple process in a specific directory. Plus, I lose platform independence.
String[] cmd = {"/bin/sh", "-c", "cd " + link + " && pwd"};
Process process = Runtime.getRuntime().exec(cmd);
String output = getOutput(process);
System.out.println(output); // Prints: /home/guido/work/path/to/symlink
Here, you can find a full example as a unit test gist with both solutions.
Upvotes: 4
Views: 1667
Reputation: 95
This was able to meet my needs with following workaround, but it looks stupid to start a shell just to be able to trigger a simple process in a specific directory. Plus, I lose platform independence.
String[] cmd = {"/bin/sh", "-c", "cd " + link + " && pwd"};
Process process = Runtime.getRuntime().exec(cmd);
String output = getOutput(process);
System.out.println(output); // Prints: /home/guido/work/path/to/symlink
https://gist.github.com/sw-samuraj/2c09157b8175b5a2365ae4c843690de0
Upvotes: 0
Reputation: 6233
As the other answer indicates there is probably no way to get this to work with symlinks, but perhaps there is another way. You could use a bind mount instead. See https://unix.stackexchange.com/questions/198590/what-is-a-bind-mount or https://backdrift.org/how-to-use-bind-mounts-in-linux for more details. In short:
mkdir -p /path/to
mount -o bind /home/guido/work /path/to/symlink
This mounts a new file system in the path as a new view to the same directories and files. The Java application should resolve the path correctly. On the other hand a symlink is much more convenient and can be created without being root, so this is an exotic solution. Nevertheless, if it is important enough, perhaps it is the way to go?
Upvotes: 1
Reputation: 86
If I understand correctly, you want the process to see the symlink as its working directory, and not the symlink target. I doubt that this is possible. A similar question has been asked for Python:
Prevent `os.chdir` from resolving symbolic link
First, the JVM doesn't resolve symlinks while running a process. After forking, it calls the chdir()
system call in the child process, and that resolves the symlink:
Next, the working directory is returned by the getcwd()
system call, which is guaranteed to return a path whose components are not symlinks:
http://pubs.opengroup.org/onlinepubs/9699919799/functions/getcwd.html
Your workaround works for Bash, but it will not work for a process that calls getcwd()
. Bash has its own version of pwd
, which (I guess) also looks at the PWD
environment variable (which is set by cd
). If you modify pwd
to /usr/bin/pwd
in your workaround, it will display the symlink target.
Upvotes: 5