Мне нужно запустить собственный процесс из Java внутри каталога символических ссылок. У нас будет следующая структура каталогов:
guido@Firefly:~/work$ tree
.
└── path
└── to
└── symlink -> /home/guido/work
3 directories, 0 files
Я запускаю приложение Java из каталога ~/work и хочу запустить собственный процесс в каталоге ~/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
Я смог удовлетворить свои потребности с помощью следующего обходного пути, но кажется глупым запускать оболочка только для того, чтобы иметь возможность запускать простой процесс в определенном каталоге. К тому же я теряю независимость от платформы.
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
Здесь вы можете найти полный пример в виде суть модульного теста с обоими решениями.
Я прошу Как запустить процесс на Java внутри каталога символических ссылок?pwd - это просто пример команды, которую мне нужно заменить на «настоящую». Немного изменю описание.
Если вы используете Path path = Files.createSymbolicLink (link.toPath (), baseDir.toPath ()); а затем File f = new File (path.toRealPath (LinkOption.NOFOLLOW_LINKS) .toString ()); вы получаете то, что ожидаете, но передача его в ProcessBuilder.directory, похоже, подразумевает, что ProcessBuilder следует символическим ссылкам, поскольку он не останется в ссылке
@EJP Я изменил описание, так что, надеюсь, стало понятнее, что я не спрашиваю о поведении символических ссылок.
Ваш тестовый пример работает должным образом / как вам нужно на моем Mac. Итак, вы можете обновить информацию о своей ОС до версии Q.
@sujit Не уверен, что вы имеете в виду - эти тесты, предоставленные через суть, имеют точно такое же поведение на Linux и на macOS (10.13.3). Итак, тест runCommandInShell проходит успешно, а тест runCommandInSymlinkDir не проходит по утверждению.
Пытался но безуспешно. Java всегда разрешает путь к настоящему. Я думаю, что это может быть невыполнимая миссия только на Java.




Если я правильно понимаю, вы хотите, чтобы процесс видел символическую ссылку в качестве своего рабочего каталога, а не цель символической ссылки. Я сомневаюсь, что это возможно. Аналогичный вопрос был задан для Python:
Запретить `os.chdir` разрешить символическую ссылку
Во-первых, JVM не разрешает символические ссылки во время выполнения процесса. После разветвления он вызывает системный вызов chdir() в дочернем процессе, который разрешает символическую ссылку:
Затем рабочий каталог возвращается системным вызовом getcwd(), который гарантированно возвращает путь, компоненты которого не являются символическими ссылками:
http://pubs.opengroup.org/onlinepubs/9699919799/functions/getcwd.html
Ваш обходной путь работает для Bash, но не работает для процесса, вызывающего getcwd(). У Bash есть собственная версия pwd, которая (я полагаю) также проверяет переменную среды PWD (которая устанавливается cd). Если вы измените pwd на /usr/bin/pwd в своем обходном пути, он отобразит цель символической ссылки.
Это очень хороший ответ, объясняющий проблему. Однако это не помогает с моей проблемой: я пишу приложение Java (плагин Gradle), поэтому я не могу контролировать среду пользователя - я могу только проверять ОС и реагировать на них.
Как указывает другой ответ, вероятно, нет способа заставить это работать с символическими ссылками, но, возможно, есть другой способ. Вместо этого вы можете использовать привязное крепление. См. https://unix.stackexchange.com/questions/198590/what-is-a-bind-mount или https://backdrift.org/how-to-use-bind-mounts-in-linux для получения более подробной информации. Коротко:
mkdir -p /path/to
mount -o bind /home/guido/work /path/to/symlink
Это монтирует новую файловую систему в пути как новое представление для тех же каталогов и файлов. Приложение Java должно правильно разрешить путь. С другой стороны, символическая ссылка намного удобнее и может быть создана без root, так что это экзотическое решение. Тем не менее, если это достаточно важно, возможно, это правильный путь?
Это удовлетворило мои потребности с помощью следующего обходного пути, но выглядит глупо запускать оболочку только для того, чтобы иметь возможность запускать простой процесс в определенном каталоге. К тому же я теряю независимость от платформы.
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
Вот что делают символические ссылки. Придерживайтесь настоящего пути. Непонятно, о чем вы спрашиваете,