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

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

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

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

13 ответов

Решение

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

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

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

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

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

Есть способ сделать это, используя системное свойство "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");
    }
}

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

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

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

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

В частности,

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

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

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

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

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

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

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

Вы можете изменить фактический рабочий каталог процесса с помощью 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 );
}

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

новый файл ("относительный / путь").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

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

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

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

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

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

Использовать 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);
Другие вопросы по тегам