Изменение текущего рабочего каталога в 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);