Зміна поточного робочого каталогу на 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 #, що змушує мене думати, "ті хлопці з Java впевнені, що вони роблять"
Джейк

2
Важко повірити, що в java немає певного параметра "start in this directory ..." принаймні ...
rogerdpack

5
Захист Java (і я хлопець UNIX, який бажає цієї функції) ... це VM, який повинен бути агностичним для деталей ОС. Ідіома "присутній робочий каталог" недоступна в деяких операційних системах.
Тоні К.

4
Щоб бути справедливим до Java, вони повинні були зробити це спочатку, C # отримала користь, що змогла вчитися на своїх помилках у багатьох областях.
Райан Ліч

3
@VolkerSeibt: При подальшому дослідженні, здається, що user.dir працює лише для деяких класів, включаючи те, з яким я тестував спочатку. new FileOutputStream("foo.txt").close();створює файл у вихідному робочому каталозі, навіть якщо user.dir змінено програмою.
Майкл Майерс

37

Якщо ви запустите свою застарілу програму за допомогою ProcessBuilder , ви зможете вказати її робочий каталог .


1
Маршрут - це маршрут, який я взяв. Мені вдалося запустити виконуваний файл з іншого робочого каталогу із наступним: Файл WorkingDir = новий файл ("C: \\ шлях \\ до \\ робочий \\ dir \\"); ProcessBuilder pBuilder = новий ProcessBuilder ("C: \\ шлях \\ до \\ робочий \\ dir \\ Executable.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, ви можете вказати робочий каталог цього сценарію, залишаючи робочий dir JVM без змін.

Див. Ruvantime.exec javadocs

Зокрема,

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

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


1
Це здається кращою відповіддю, ніж прийнята відповідь (яка починається з "Немає надійного способу зробити це в чистому Java."). Чи існує спосіб подання клопотання про те, щоб ця відповідь була розглянута як прийнята відповідь?
Іван

11

Якщо я правильно розумію, програма Java починається з копії змінних поточного середовища. Будь-які зміни через - System.setProperty(String, String)це модифікація копії, а не оригінальних змінних середовища. Не те, що це є ґрунтовною причиною того, чому НД обрала таку поведінку, але, можливо, вона проливає трохи світла ...


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)

Оскільки фрагмент коду містить жорстко закодований шлях до файлу і, ймовірно, використовує жорстко закодований конструктор, який не вказує батьківський каталог для роботи. Принаймні, така у мене ситуація :)
Девід Манн

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

Він створює файл Об'єкт як абсолютний з поточним каталогом, який відомий JVM. Але цей код повинен існувати у використаному класі, він потребує зміни повторно використаних кодів.

~~~~ JcHartmut


"Це створює файл" Об'єкт "- так. Але це не створює файл у файловій системі. Ви забули використовувати файл.createNewFile () після цього.
Gangnus

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

1
Зауважте, що така поведінка змінилася в Java 11, див. Bugs.openjdk.java.net/browse/JDK-8202127 . Вони не рекомендують користуватисяSystem.setProperty("user.dir", "/some/directory")
Мартін

0

Інша можлива відповідь на це питання може залежати від причини відкриття файлу. Це файл властивостей або файл, який має певну конфігурацію, пов’язану з вашою програмою?

У цьому випадку ви можете спробувати завантажити файл через завантажувач classpath, таким чином ви можете завантажити будь-який файл, до якого Java має доступ.


0

Якщо ви запускаєте свої команди в оболонці, ви можете написати щось на кшталт "java -cp" і додати будь-які каталоги, які ви хочете розділити на ":" якщо java не знайде щось в одному каталозі, він спробує знайти їх в інших каталогах, це те, що я роблю.


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

імпорт javax.swing.filechooser.FileSystemView;
Борнек

1
Ця відповідь не пов'язана з питанням.
Аарон Дігулла
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.