Как запустить процесс на Java внутри каталога символических ссылок?

Мне нужно запустить собственный процесс из 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

Здесь вы можете найти полный пример в виде суть модульного теста с обоими решениями.

Вот что делают символические ссылки. Придерживайтесь настоящего пути. Непонятно, о чем вы спрашиваете,

user207421 03.04.2018 11:50

Я прошу Как запустить процесс на Java внутри каталога символических ссылок?pwd - это просто пример команды, которую мне нужно заменить на «настоящую». Немного изменю описание.

Vít Kotačka 03.04.2018 11:54

Если вы используете Path path = Files.createSymbolicLink (link.toPath (), baseDir.toPath ()); а затем File f = new File (path.toRealPath (LinkOption.NOFOLLOW_LINKS) .toString ()); вы получаете то, что ожидаете, но передача его в ProcessBuilder.directory, похоже, подразумевает, что ProcessBuilder следует символическим ссылкам, поскольку он не останется в ссылке

codebrane 03.04.2018 12:25

@EJP Я изменил описание, так что, надеюсь, стало понятнее, что я не спрашиваю о поведении символических ссылок.

Vít Kotačka 03.04.2018 14:18

Ваш тестовый пример работает должным образом / как вам нужно на моем Mac. Итак, вы можете обновить информацию о своей ОС до версии Q.

sujit 06.04.2018 14:52

@sujit Не уверен, что вы имеете в виду - эти тесты, предоставленные через суть, имеют точно такое же поведение на Linux и на macOS (10.13.3). Итак, тест runCommandInShell проходит успешно, а тест runCommandInSymlinkDir не проходит по утверждению.

Vít Kotačka 06.04.2018 15:31

Пытался но безуспешно. Java всегда разрешает путь к настоящему. Я думаю, что это может быть невыполнимая миссия только на Java.

terry.qiao 06.04.2018 15:42
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
4
7
928
3

Ответы 3

Если я правильно понимаю, вы хотите, чтобы процесс видел символическую ссылку в качестве своего рабочего каталога, а не цель символической ссылки. Я сомневаюсь, что это возможно. Аналогичный вопрос был задан для 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 11.06.2018 09:45

Как указывает другой ответ, вероятно, нет способа заставить это работать с символическими ссылками, но, возможно, есть другой способ. Вместо этого вы можете использовать привязное крепление. См. 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

Другие вопросы по теме