如何在Java中运行一个位于符号链接目录中的进程?

4

我需要在符号链接目录中从Java运行本地进程。让我们创建以下目录结构:

guido@Firefly:~/work$ tree
.
└── path
    └── to
        └── symlink -> /home/guido/work

3 directories, 0 files

我正在从〜/work目录启动Java应用程序,并希望在〜/work/path/to/symlink目录中运行本机进程。
然而,如果我使用以下Java代码,则符号链接工作目录将解析为真实路径。 相反,我想在绝对路径中运行该命令。
(请注意,pwd命令只是为了说明,并且应该替换为“真正的”命令(例如,在我的情况下使用go build)。)
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

我能通过以下解决方法满足我的需求,但是为了能够在特定目录下触发一个简单的进程而启动一个 shell 看起来很愚蠢。此外,我失去了平台独立性。
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

在这里,您可以找到一个完整的示例,其中包含单元测试Gist和两种解决方案。

1
这就是符号链接的作用。解析为真实路径。不清楚你正在询问什么。 - user207421
我正在询问如何在符号链接目录中运行Java进程?pwd只是一个示例命令,我需要用“真实”的命令来替换它。我将稍微修改一下描述。 - Vít Kotačka
如果您使用Path path = Files.createSymbolicLink(link.toPath(), baseDir.toPath());,然后File f = new File(path.toRealPath(LinkOption.NOFOLLOW_LINKS).toString());,您将得到预期的结果,但将其传递给ProcessBuilder.directory似乎意味着ProcessBuilder会遵循符号链接,因为它不会停留在链接中。 - codebrane
@EJP,我修改了描述,希望更清楚地表明我不是在询问符号链接的行为。 - Vít Kotačka
尝试过,但没有成功。Java总是解析路径到真实路径。我认为这可能只能在Java中完成,是一个不可能的任务。 - terry.qiao
显示剩余2条评论
3个回答

5
如果我理解正确,您希望进程将符号链接视为其工作目录,而不是符号链接目标。我怀疑这是不可能的。类似的问题已经被提出了Python:

防止`os.chdir`解析符号链接

首先,JVM在运行进程时不会解析符号链接。在分叉之后,它会在子进程中调用chdir()系统调用,然后解析符号链接:

https://github.com/dmlloyd/openjdk/blob/342a565a2da8abd69c4ab85e285bb5f03b48b2c9/src/java.base/unix/native/libjava/childproc.c#L362

接下来,getcwd()系统调用返回的是工作目录,保证其组成部分不是符号链接:

http://pubs.opengroup.org/onlinepubs/9699919799/functions/getcwd.html

你的解决方法适用于Bash,但是对于调用getcwd()的进程无效。Bash有自己的pwd版本,它(我猜)也查看PWD环境变量(由cd设置)。如果你在你的解决方法中修改pwd/usr/bin/pwd,它将显示符号链接目标。

这是一个非常好的回答,解释了问题。然而,它并没有帮助我解决我的问题:我正在编写一个Java应用程序(一个_Gradle_插件),因此我无法控制用户的环境 - 我只能进行操作系统检查并对其做出反应。 - Vít Kotačka

1

正如其他答案所指出的那样,可能没有办法使用符号链接使其工作,但也许有另一种方法。您可以使用绑定挂载代替。有关详细信息,请参见https://unix.stackexchange.com/questions/198590/what-is-a-bind-mounthttps://backdrift.org/how-to-use-bind-mounts-in-linux。简而言之:

mkdir -p /path/to
mount -o bind /home/guido/work /path/to/symlink

这会在路径中挂载一个新的文件系统,作为相同目录和文件的新视图。Java应用程序应正确解析该路径。另一方面,符号链接更加方便,可以在非root用户下创建,因此这是一种奇特的解决方案。不过,如果很重要,也许这就是前进的方式?


0

这个解决方法能够满足我的需求,但是为了在特定目录下触发一个简单的进程而启动一个 shell 看起来很愚蠢。此外,我失去了平台独立性。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接