Mengubah direktori kerja saat ini di Jawa?

169

Bagaimana saya bisa mengubah direktori kerja saat ini dari dalam program Java? Segala sesuatu yang saya dapat temukan tentang masalah ini mengklaim bahwa Anda tidak dapat melakukannya, tetapi saya tidak dapat percaya bahwa itulah masalahnya.

Saya memiliki sepotong kode yang membuka file menggunakan jalur file relatif yang dikodekan keras dari direktori yang biasanya dimulai, dan saya hanya ingin dapat menggunakan kode itu dari dalam program Java yang berbeda tanpa harus memulainya dari dalam direktori tertentu. Sepertinya Anda seharusnya bisa menelepon System.setProperty( "user.dir", "/path/to/dir" ), tetapi sejauh yang saya tahu, memanggil saluran itu hanya gagal dan tidak melakukan apa-apa.

Saya akan mengerti jika Java tidak memperbolehkan Anda untuk melakukan ini, jika bukan karena fakta bahwa itu memungkinkan Anda untuk mendapatkan direktori kerja saat ini, dan bahkan memungkinkan Anda untuk membuka file menggunakan jalur file relatif ....

Nick
sumber
1
Mendapatkan dan menggunakan informasi berbeda dengan mengubahnya. Sebagai contoh pada Windows Anda dapat dengan mudah mendapatkan variabel lingkungan tetapi lebih sulit untuk mengubahnya (dalam sistem lebar).
PhiLho
1
bugs.java.com/bugdatabase/view_bug.do?bug_id=4045688 menyatakan di bagian evaluasi "Sejak saat itu, tidak ada pelanggan lebih lanjut yang maju atau diidentifikasi. ..." dan pada 2018 kami punya sekitar 175.000 dilihat dari pertanyaan ini :-(
Wolfgang Fahl

Jawaban:

146

Tidak ada cara yang dapat diandalkan untuk melakukan ini di Jawa murni. Mengatur user.dirproperti melalui System.setProperty()atau java -Duser.dir=...tampaknya mempengaruhi kreasi berikutnya Files, tetapi tidak misalnya FileOutputStreams.

The File(String parent, String child)konstruktor dapat membantu jika Anda membangun jalur direktori Anda terpisah dari path file Anda, memungkinkan swapping lebih mudah.

Alternatifnya adalah dengan mengatur skrip untuk menjalankan Java dari direktori yang berbeda, atau menggunakan kode asli JNI seperti yang disarankan di bawah ini .

Bug Sun yang relevan ditutup pada 2008 karena "tidak akan diperbaiki".

Michael Myers
sumber
12
saya tidak berpikir saya telah menemukan perbedaan tunggal antara java dan c # yang membuat saya berpikir, "orang-orang java itu pasti tahu apa yang mereka lakukan"
Jake
2
Sulit untuk percaya bahwa java tidak memiliki beberapa parameter "mulai dalam direktori ini ..." setidaknya ...
rogerdpack
5
Dalam pertahanan Java (dan saya seorang pria UNIX yang menginginkan fitur ini) ... itu adalah VM yang dimaksudkan sebagai agnostik untuk rincian OS. Ungkapan "direktori kerja saat ini" tidak tersedia di beberapa sistem operasi.
Tony K.
4
Agar adil di Jawa, mereka harus melakukannya terlebih dahulu, C # memiliki manfaat untuk dapat belajar dari kesalahan mereka di banyak daerah.
Ryan The Leach
3
@VolkerSeibt: Pada penyelidikan lebih lanjut, tampaknya user.dir hanya berfungsi untuk beberapa kelas, termasuk yang saya uji pada awalnya. new FileOutputStream("foo.txt").close();membuat file di direktori kerja asli bahkan jika user.dir diubah oleh program.
Michael Myers
37

Jika Anda menjalankan program lawas Anda dengan ProcessBuilder , Anda akan dapat menentukan direktori kerjanya .

PhiLho
sumber
1
Rute adalah rute yang saya ambil. Saya dapat menjalankan executable dari direktori kerja yang berbeda dengan yang berikut ini: File WorkingDir = File baru ("C: \\ path \\ to \\ working \\ dir \\"); ProcessBuilder pBuilder = ProcessBuilder baru ("C: \\ path \\ to \\ working \\ dir \\ executable.exe"); pBuilder.directory (WorkingDir); Proses p = pBuilder.start ();
CatsAndCode
29

Ada adalah cara untuk melakukan hal ini menggunakan properti sistem "user.dir". Bagian penting untuk dipahami adalah getAbsoluteFile () harus dipanggil (seperti yang ditunjukkan di bawah ini) atau jalur relatif akan diselesaikan terhadap nilai "user.dir" 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");
    }
}
Steve K.
sumber
3
path absolut sangat penting
HackNone
5
Tetapi itu tidak mengubah direktori kerja saat ini. Hanya nilai user.dir. Fakta bahwa jalan absolut menjadi kritis membuktikannya.
Marquis of Lorne
18

Dimungkinkan untuk mengubah PWD, menggunakan JNA / JNI untuk membuat panggilan ke libc. Orang-orang JRuby memiliki perpustakaan java yang berguna untuk membuat panggilan POSIX disebut jna-posix Inilah info pakar

Anda dapat melihat contoh penggunaannya di sini (kode Clojure, maaf). Lihatlah fungsi chdirToRoot

Allen Rohner
sumber
1
Sepertinya tidak ada versi modern dari jna-posix. Saya bercabang dan menambahkan satu: github.com/pbiggar/jnr-posix . Saya dapat mengkonfirmasi bahwa saya dapat mengubah PWD dengan ini.
Paul Biggar
Iya. Maaf, saya refactored file itu dan lupa jawaban ini tertaut ke file. Tetap.
Allen Rohner
Anda dapat melakukan ini, tetapi jika Anda tidak juga mengubah user.dirproperti sistem maka File.getAbsolutePath()akan memutuskan melawan user.dir, sedangkan pathname di File menyelesaikan terhadap direktori kerja OS.
Morrie
Sehubungan dengan pertanyaan awal, menggunakan jnr-posix, bagaimana saya bisa mengubah direktori kerja saat ini di Java. Kelas apa yang harus saya buat untuk menggunakan metode chdir? Saya tidak terlalu mengerti contoh Clojure yang diberikan. Terima kasih sebelumnya.
Sam Saint-Pettersen
12

Seperti yang disebutkan Anda tidak dapat mengubah CWD dari JVM tetapi jika Anda meluncurkan proses lain menggunakan Runtime.exec () Anda dapat menggunakan metode kelebihan beban yang memungkinkan Anda menentukan direktori kerja. Ini bukan benar-benar untuk menjalankan program Java Anda di direktori lain tetapi untuk banyak kasus ketika seseorang perlu meluncurkan program lain seperti skrip Perl misalnya, Anda dapat menentukan direktori kerja skrip tersebut sambil membiarkan direktori kerja JVM tidak berubah.

Lihat Runtime.exec javadocs

Secara khusus,

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

di mana dirdirektori kerja untuk menjalankan subproses di

Bizmarck
sumber
1
Ini tampaknya menjadi jawaban yang lebih baik daripada jawaban yang diterima (yang dimulai dengan "Tidak ada cara yang dapat diandalkan untuk melakukan ini di Jawa murni."). Apakah ada cara untuk mengajukan petisi agar jawaban ini dianggap sebagai jawaban yang diterima?
John
11

Jika saya mengerti benar, program Java dimulai dengan salinan variabel lingkungan saat ini. Setiap perubahan via System.setProperty(String, String)memodifikasi salinan, bukan variabel lingkungan asli. Bukannya ini memberikan alasan menyeluruh mengapa Sun memilih perilaku ini, tapi mungkin itu sedikit memberi ...

Adam Paynter
sumber
2
Anda tampaknya mencampur variabel dan properti lingkungan. Yang pertama akan diwarisi dari OS sementara yang terakhir dapat didefinisikan pada baris perintah menggunakan -D. Tapi saya setuju, pada JVM mulai properti yang telah ditentukan seperti user.dirdisalin dari OS dan mengubahnya nanti tidak membantu.
maaartinus
Mengubah user.dirmempengaruhi File.getAbsolutePath()dan File.getCanonicalPath(), tetapi bukan gagasan OS tentang direktori kerja, yang menentukan bagaimana nama path file diselesaikan ketika mengakses file.
Morrie
5

Direktori kerja adalah fitur sistem operasi (ditetapkan saat proses dimulai). Mengapa Anda tidak melewati properti Sistem Anda sendiri ( -Dsomeprop=/my/path) dan menggunakannya dalam kode Anda sebagai induk dari File Anda:

File f = new File ( System.getProperty("someprop"), myFilename)
raphaëλ
sumber
Karena potongan kode berisi path file kode keras, dan mungkin menggunakan konstruktor kode keras yang tidak menentukan direktori induk untuk bekerja. Atleast, begitulah situasinya :)
David Mann
4

Yang lebih pintar / lebih mudah untuk dilakukan di sini adalah hanya mengubah kode Anda sehingga alih-alih membuka file dengan asumsi bahwa itu ada di direktori kerja saat ini (saya berasumsi Anda sedang melakukan sesuatu seperti new File("blah.txt"), hanya membangun path ke file sendiri.

Biarkan pengguna lewat di direktori basis, bacalah dari file konfigurasi, kembalilah ke user.dirjika properti lain tidak dapat ditemukan, dll. Tetapi jauh lebih mudah untuk meningkatkan logika dalam program Anda daripada mengubah cara variabel lingkungan bekerja.

matt b
sumber
2

Saya sudah mencoba memohon

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

Sepertinya berhasil. Tapi

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

melempar FileNotFoundExceptionmeskipun

myFile.getAbsolutePath()

menunjukkan jalur yang benar. Saya sudah membaca ini . Saya pikir masalahnya adalah:

  • Java tahu direktori saat ini dengan pengaturan baru.
  • Namun penanganan file dilakukan oleh sistem operasi. Sayangnya, ia tidak mengetahui direktori set yang baru.

Solusinya mungkin:

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

Itu menciptakan objek file sebagai mutlak dengan direktori saat ini yang dikenal oleh JVM. Tetapi kode itu harus ada di kelas yang digunakan, perlu mengubah kode yang digunakan kembali.

~~~~ JcHartmut

Hartmut Schorrig
sumber
"Ini menciptakan Obyek file" - ya. Tapi itu tidak membuat file di sistem file. Anda lupa menggunakan file.createNewFile () setelah itu.
Gangnus
2

Kamu bisa memakai

File baru ("relatif / jalur"). getAbsoluteFile ()

setelah

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

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

Akan dicetak

C:\OtherProject\data\data.csv
javacommons
sumber
1
Harap perhatikan bahwa perilaku ini berubah di Java 11, lihat bugs.openjdk.java.net/browse/JDK-8202127 . Mereka tidak merekomendasikan penggunaanSystem.setProperty("user.dir", "/some/directory")
Martin
0

Jawaban lain yang mungkin untuk pertanyaan ini mungkin tergantung pada alasan Anda membuka file. Apakah ini file properti atau file yang memiliki beberapa konfigurasi yang terkait dengan aplikasi Anda?

Jika hal ini Anda mungkin mempertimbangkan untuk mencoba memuat file melalui loader classpath, dengan cara ini Anda dapat memuat file apa pun yang memiliki akses Java.

Nathan Feger
sumber
0

Jika Anda menjalankan perintah dalam sebuah shell, Anda dapat menulis sesuatu seperti "java -cp" dan menambahkan direktori yang ingin Anda pisahkan dengan ":" jika java tidak menemukan sesuatu dalam satu direktori, ia akan mencoba dan menemukannya di direktori lain, yang adalah apa yang saya lakukan.

lesolorzanov
sumber
0

Anda dapat mengubah direktori kerja proses yang sebenarnya menggunakan JNI atau JNA.

Dengan JNI, Anda dapat menggunakan fungsi asli untuk mengatur direktori. Metode POSIX adalah chdir(). Di Windows, Anda bisa menggunakan SetCurrentDirectory().

Dengan JNA, Anda dapat membungkus fungsi-fungsi asli dalam Java binder.

Untuk Windows:

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

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

Untuk sistem POSIX:

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

    /** int chdir(const char *path); */
    int chdir( String path );
}
Andy Thomas
sumber
-1

Gunakan 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
sumber
import javax.swing.filechooser.FileSystemView;
Borneq
1
Jawaban ini tidak terkait dengan pertanyaan.
Aaron Digulla