Изменение текущего рабочего каталога в Java?

169

Как я могу изменить текущий рабочий каталог из программы Java? Все, что я смог найти в этой проблеме, утверждает, что вы просто не можете этого сделать, но я не могу поверить, что это действительно так.

У меня есть фрагмент кода, который открывает файл с использованием жестко заданного относительного пути к файлу из каталога, в котором он обычно запускается, и я просто хочу иметь возможность использовать этот код из другой Java-программы, не запуская его изнутри конкретный каталог. Кажется, что вы должны просто быть в состоянии звонить System.setProperty( "user.dir", "/path/to/dir" ), но, насколько я могу понять, звонить по этой линии просто молча не получается и ничего не делает.

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

Ник
источник
1
Получение и использование информации отличается от ее изменения. Например, в Windows вы можете легко получить переменные окружения, но их сложнее изменить (общесистемным способом).
PhiLho
1
bugs.java.com/bugdatabase/view_bug.do?bug_id=4045688 заявляет в разделе оценки «С того времени больше не было клиентов или не было идентифицировано иным образом ... ...», и по состоянию на 2018 год мы получили около 175 000 взгляды на этот вопрос :-(
Вольфганг Фаль

Ответы:

146

Не существует надежного способа сделать это на чистой Java. Установка user.dirсвойства через System.setProperty()или java -Duser.dir=..., кажется, влияет на последующее создание Files, но не например FileOutputStreams.

File(String parent, String child)Конструктор может помочь , если вы строите свой путь к каталогу отдельно от пути к файлу, что позволяет легче обменивать.

Альтернатива - настроить скрипт для запуска Java из другого каталога или использовать собственный код JNI, как предложено ниже .

Соответствующая ошибка Sun была закрыта в 2008 году как «не будет исправлена».

Майкл Майерс
источник
12
я не думаю , что я нашел один разницу между Java и C # , что заставляет меня думать, «эти ява ребята уверены , знают , что они делают»
Jake
2
Трудно поверить, что у java нет какого-либо параметра «начать в этом каталоге ...», по крайней мере ...
rogerdpack
5
В защиту Java (а я парень из UNIX, которому нужна эта функция) ... это виртуальная машина, которая должна быть независимой от особенностей ОС. Идиома «текущий рабочий каталог» недоступна в некоторых операционных системах.
Тони К.
4
Чтобы быть справедливым по отношению к Java, они должны были сделать это в первую очередь, у C # было то преимущество, что они могли учиться на своих ошибках во многих областях.
Райан Лич
3
@VolkerSeibt: при дальнейших исследованиях кажется, что user.dir работает только для некоторых классов, включая тот, который я тестировал сначала. new FileOutputStream("foo.txt").close();создает файл в исходном рабочем каталоге, даже если программа изменила user.dir.
Майкл Майерс
37

Если вы запустите свою унаследованную программу с ProcessBuilder , вы сможете указать ее рабочий каталог .

PhiLho
источник
1
«Маршрут» - это маршрут, по которому я пошел. Мне удалось запустить исполняемый файл из другого рабочего каталога со следующим: File WorkingDir = new File ("C: \\ path \\ to \\ working \\ dir \\"); ProcessBuilder pBuilder = новый ProcessBuilder ("C: \\ path \\ to \\ working \\ dir \\ executetable.exe"); pBuilder.directory (WorkingDir); Процесс p = pBuilder.start ();
CatsAndCode
29

Там является способ сделать это , используя системное свойство «user.dir». Ключевая часть, которую нужно понять, - это то, что getAbsoluteFile () должен быть вызван (как показано ниже), иначе относительные пути будут разрешены в соответствии со значением по умолчанию «user.dir».

import java.io.*;

public class FileUtils
{
    public static boolean setCurrentDirectory(String directory_name)
    {
        boolean result = false;  // Boolean indicating whether directory was set
        File    directory;       // Desired current working directory

        directory = new File(directory_name).getAbsoluteFile();
        if (directory.exists() || directory.mkdirs())
        {
            result = (System.setProperty("user.dir", directory.getAbsolutePath()) != null);
        }

        return result;
    }

    public static PrintWriter openOutputFile(String file_name)
    {
        PrintWriter output = null;  // File to open for writing

        try
        {
            output = new PrintWriter(new File(file_name).getAbsoluteFile());
        }
        catch (Exception exception) {}

        return output;
    }

    public static void main(String[] args) throws Exception
    {
        FileUtils.openOutputFile("DefaultDirectoryFile.txt");
        FileUtils.setCurrentDirectory("NewCurrentDirectory");
        FileUtils.openOutputFile("CurrentDirectoryFile.txt");
    }
}
Стив К
источник
3
абсолютный путь критически важен
HackNone
5
Но это не меняет текущий рабочий каталог. Только ценность user.dir. Тот факт, что абсолютный путь становится критическим, доказывает это.
Маркиз Лорн
18

Можно изменить PWD, используя JNA / JNI для вызова libc. Ребята из JRuby имеют удобную библиотеку Java для выполнения вызовов POSIX, которая называется jna-posix. Вот информация о maven.

Вы можете увидеть пример его использования здесь (код Clojure, извините). Посмотрите на функцию chdirToRoot

Аллен Ронер
источник
1
Кажется, не существует современной версии jna-posix. Я раздвоил и добавил один: github.com/pbiggar/jnr-posix . Я могу подтвердить, что я могу изменить PWD с этим.
Пол Биггар
Да. Извините, я реорганизовал этот файл и забыл ответ, связанный с файлом. Исправлена.
Аллен Ронер
Вы можете сделать это, но если вы также не измените user.dirсистемное свойство, File.getAbsolutePath()произойдет сопоставление user.dir, в то время как путь в Файле сопоставляется с рабочим каталогом ОС.
Морри
Относительно исходного вопроса, используя jnr-posix, как я могу изменить текущий рабочий каталог в Java. Какой класс мне нужно создать, чтобы использовать метод chdir? Я не очень понял пример Clojure. Заранее спасибо.
Сэм Сен-Петтерсен
12

Как уже упоминалось, вы не можете изменить CWD JVM, но если вы должны были запустить другой процесс с помощью Runtime.exec (), вы можете использовать перегруженный метод, который позволяет вам указать рабочий каталог. Это на самом деле не для запуска вашей Java-программы в другом каталоге, но во многих случаях, когда нужно запустить другую программу, например, скрипт Perl, вы можете указать рабочий каталог этого скрипта, оставив рабочий каталог JVM без изменений.

Смотрите Runtime.exec javadocs

В частности,

public Process exec(String[] cmdarray,String[] envp, File dir) throws IOException

где dirнаходится рабочий каталог для запуска подпроцесса

Bizmarck
источник
1
Похоже, что это лучший ответ, чем принятый ответ (который начинается с «Нет надежного способа сделать это на чистой Java»). Можно ли подать петицию, чтобы этот ответ считался принятым ответом?
Джон
11

Если я правильно понимаю, Java-программа начинается с копии текущих переменных среды. Любые изменения через System.setProperty(String, String)изменяют копию, а не исходные переменные среды. Не то чтобы это дало основательную причину тому, почему Sun выбрала такое поведение, но, возможно, оно проливает немного света ...

Адам Пейнтер
источник
2
Вы, кажется, смешиваете переменные окружения и свойства. Первое наследуется от ОС, а второе можно определить с помощью командной строки -D. Но я согласен, при запуске JVM предопределенные свойства, такие как user.dirкопирование из ОС и их последующее изменение, не помогают.
Maaartinus
Изменение user.dirвлияет File.getAbsolutePath()и File.getCanonicalPath(), но не на представление ОС о рабочем каталоге, которое определяет способ разрешения имен файлов при доступе к файлам.
Морри
5

Рабочий каталог - это функция операционной системы (устанавливается при запуске процесса). Почему бы вам просто не передать собственное свойство System ( -Dsomeprop=/my/path) и использовать его в своем коде в качестве родителя вашего файла:

File f = new File ( System.getProperty("someprop"), myFilename)
raphaëλ
источник
Поскольку фрагмент кода содержит жестко закодированный путь к файлу и, вероятно, использует жестко запрограммированный конструктор, который не указывает родительский каталог для работы. По крайней мере, у меня такая ситуация :)
Дэвид Манн,
4

Умнее / проще сделать здесь - просто изменить свой код так, чтобы вместо открытия файла предполагалось, что он существует в текущем рабочем каталоге (я предполагаю, что вы делаете что-то вроде new File("blah.txt"), просто создайте путь к файлу самостоятельно.

Пусть пользователь перейдет в базовый каталог, прочитает его из файла конфигурации, обратится к тому, user.dirесли другие свойства не могут быть найдены, и т. Д. Но гораздо проще улучшить логику в вашей программе, чем изменить способ переменные окружения работают.

Мэтт Б
источник
2

Я пытался вызвать

String oldDir = System.setProperty("user.dir", currdir.getAbsolutePath());

Вроде работает. Но

File myFile = new File("localpath.ext"); InputStream openit = new FileInputStream(myFile);

бросает FileNotFoundExceptionхотя

myFile.getAbsolutePath()

показывает правильный путь. Я прочитал это . Я думаю, что проблема заключается в:

  • Java знает текущий каталог с новой настройкой.
  • Но обработка файла выполняется операционной системой. Он не знает новый набор текущего каталога, к сожалению.

Решение может быть:

File myFile = new File(System.getPropety("user.dir"), "localpath.ext");

Он создает файл Object как абсолютный с текущим каталогом, который известен JVM. Но этот код должен существовать в используемом классе, он требует замены повторно используемых кодов.

~~~~ JcHartmut

Хартмут Шорриг
источник
«Создает файл Object» - да. Но это не создает файл в файловой системе. Вы забыли использовать file.createNewFile () после этого.
Гангнус
2

Ты можешь использовать

новый файл ("относительный / путь"). getAbsoluteFile ()

после

System.setProperty ("user.dir", "/ some / directory")

System.setProperty("user.dir", "C:/OtherProject");
File file = new File("data/data.csv").getAbsoluteFile();
System.out.println(file.getPath());

Будет печатать

C:\OtherProject\data\data.csv
javacommons
источник
1
Обратите внимание, что это поведение изменилось в Java 11, см. Bugs.openjdk.java.net/browse/JDK-8202127 . Они не рекомендуют использоватьSystem.setProperty("user.dir", "/some/directory")
Martin
0

Другой возможный ответ на этот вопрос может зависеть от причины, по которой вы открываете файл. Это файл свойств или файл, имеющий некоторую конфигурацию, связанную с вашим приложением?

В этом случае вы можете попытаться загрузить файл через загрузчик classpath, таким образом вы можете загрузить любой файл, к которому у Java есть доступ.

Натан Фегер
источник
0

Если вы запускаете свои команды в оболочке, вы можете написать что-то вроде «java -cp» и добавить любые каталоги, которые вы хотите, разделив «:», если java не находит что-то в одном каталоге, он попытается найти их в других каталогах, что это то, что я делаю

lesolorzanov
источник
0

Вы можете изменить фактический рабочий каталог процесса, используя JNI или JNA.

С JNI вы можете использовать встроенные функции для установки каталога. Метод POSIX есть chdir(). На Windows вы можете использовать SetCurrentDirectory().

С JNA вы можете обернуть нативные функции в связыватели Java.

Для Windows:

private static interface MyKernel32 extends Library {
    public MyKernel32 INSTANCE = (MyKernel32) Native.loadLibrary("Kernel32", MyKernel32.class);

    /** BOOL SetCurrentDirectory( LPCTSTR lpPathName ); */
    int SetCurrentDirectoryW(char[] pathName);
}

Для систем POSIX:

private interface MyCLibrary extends Library {
    MyCLibrary INSTANCE = (MyCLibrary) Native.loadLibrary("c", MyCLibrary.class);

    /** int chdir(const char *path); */
    int chdir( String path );
}
Энди Томас
источник
-1

Использовать FileSystemView

private FileSystemView fileSystemView;
fileSystemView = FileSystemView.getFileSystemView();
currentDirectory = new File(".");
//listing currentDirectory
File[] filesAndDirs = fileSystemView.getFiles(currentDirectory, false);
fileList = new ArrayList<File>();
dirList = new ArrayList<File>();
for (File file : filesAndDirs) {
if (file.isDirectory())
    dirList.add(file);
else
    fileList.add(file);
}
Collections.sort(dirList);
if (!fileSystemView.isFileSystemRoot(currentDirectory))
    dirList.add(0, new File(".."));
Collections.sort(fileList);
//change
currentDirectory = fileSystemView.getParentDirectory(currentDirectory);
Borneq
источник
import javax.swing.filechooser.FileSystemView;
Борнек
1
Этот ответ не связан с вопросом.
Аарон Дигулла