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


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

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

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

13 ответов:

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

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

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

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

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

Существует способ сделать это с помощью системного свойства "пользователь.dir". Ключевая часть, которую нужно понять, заключается в том, что getAbsoluteFile() должен быть вызван (как показано ниже), иначе относительные пути будут разрешены против пользователя default".реж" значение.

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");
    }
}

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

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

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

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

См. Время выполнения.exec javadocs

В частности,

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

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

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

File f = new File ( System.getProperty("someprop"), myFilename)

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

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

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

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");

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

~~~~JcHartmut

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

Новый файл ("relative / path").getAbsoluteFile ()

После

Система.setProperty ("пользователь.реж.", "/некоторые / каталоги")

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

Напечатает

C:\OtherProject\data\data.csv

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

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

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

Use 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);