Thay đổi thư mục làm việc hiện tại trong Java?


169

Làm cách nào để thay đổi thư mục làm việc hiện tại từ trong chương trình Java? Mọi thứ tôi có thể tìm thấy về vấn đề này đều đơn giản là bạn không thể làm được, nhưng tôi không thể tin đó là sự thật.

Tôi có một đoạn mã mở tệp bằng cách sử dụng đường dẫn tệp tương đối được mã hóa cứng từ thư mục mà nó thường bắt đầu và tôi chỉ muốn có thể sử dụng mã đó từ bên trong một chương trình Java khác mà không phải khởi động từ bên trong một thư mục cụ thể. Có vẻ như bạn chỉ có thể gọi System.setProperty( "user.dir", "/path/to/dir" ), nhưng theo như tôi có thể nhận ra, việc gọi đường dây đó chỉ âm thầm thất bại và không làm gì cả.

Tôi sẽ hiểu nếu Java không cho phép bạn làm điều này, nếu thực tế là nó không cho phép bạn lấy thư mục làm việc hiện tại và thậm chí cho phép bạn mở các tệp bằng đường dẫn tệp tương đối ....


1
Nhận và sử dụng thông tin khác với thay đổi nó. Ví dụ trên Windows, bạn có thể dễ dàng nhận được các biến môi trường nhưng khó thay đổi chúng hơn (theo cách rộng của hệ thống).
PhiLho

1
bug.java.com/bugdatabase/view_orms.do?orms_id=4045688 trong phần đánh giá "Kể từ thời điểm đó, không có khách hàng nào tiếp tục hoặc được xác định khác. ..." và đến năm 2018, chúng tôi đã nhận được 175.000 quan điểm của câu hỏi này :-(
Wolfgang Fahl

Câu trả lời:


146

Không có cách đáng tin cậy để làm điều này trong Java thuần túy. Đặt thuộc user.dirtính thông qua System.setProperty()hoặc java -Duser.dir=...dường như ảnh hưởng đến các sáng tạo tiếp theo Files, nhưng không, ví dụ FileOutputStreams.

Hàm File(String parent, String child)tạo có thể trợ giúp nếu bạn xây dựng đường dẫn thư mục riêng biệt với đường dẫn tệp của mình, cho phép trao đổi dễ dàng hơn.

Một cách khác là thiết lập một tập lệnh để chạy Java từ một thư mục khác hoặc sử dụng mã gốc JNI như được đề xuất bên dưới .

Lỗi Sun có liên quan đã bị đóng cửa vào năm 2008 là "sẽ không sửa".


12
Tôi không nghĩ rằng tôi đã tìm thấy một sự khác biệt duy nhất giữa java và c # khiến tôi nghĩ, "những người java đó chắc chắn biết họ đang làm gì"
Jake

2
Khó có thể tin rằng java không có một số tham số "bắt đầu trong thư mục này ..." ít nhất là ...
rogerdpack

5
Để bảo vệ Java (và tôi là một người UNIX mong muốn tính năng này) ... đó là một VM có nghĩa là không thể tin được đối với các chi tiết của HĐH. Thành ngữ "thư mục làm việc hiện tại" không có sẵn trong một số hệ điều hành.
Tony K.

4
Để công bằng với Java, họ phải làm điều đó trước tiên, C # đã có lợi ích là có thể học hỏi từ những sai lầm của họ trong nhiều lĩnh vực.
Ryan The Leach

3
@VolkerSeibt: Khi điều tra thêm, có vẻ như user.dir chỉ hoạt động đối với một số lớp, bao gồm cả lớp tôi đã thử nghiệm lúc đầu. new FileOutputStream("foo.txt").close();tạo tập tin trong thư mục làm việc ban đầu ngay cả khi user.dir bị chương trình thay đổi.
Michael Myers

37

Nếu bạn chạy chương trình kế thừa của mình với ProcessBuilder , bạn sẽ có thể chỉ định thư mục làm việc của nó .


1
Tuyến đường của tôi là tuyến đường tôi đi. Tôi đã có thể chạy một tệp thực thi từ một thư mục làm việc khác với các mục sau: File WorkDir = new File ("C: \\ path \\ to \\ work \\ dir \\"); ProcessBuilder pBuilder = new ProcessBuilder ("C: \\ path \\ to \\ work \\ dir \\ execable.exe"); pBuilder.directory (WorkDir); Quá trình p = pBuilder.start ();
MèoAndCode

29

một cách để làm điều này bằng cách sử dụng hệ thống sở hữu "user.dir". Phần quan trọng để hiểu là getAbsoluteFile () phải được gọi (như được hiển thị bên dưới) nếu không các đường dẫn tương đối khác sẽ được giải quyết theo giá trị "user.dir" mặc định .

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
đường dẫn tuyệt đối là rất quan trọng
HackNone

5
Nhưng nó không thay đổi thư mục làm việc hiện tại. Chỉ có giá trị của user.dir. Thực tế là con đường tuyệt đối trở nên quan trọng chứng minh điều đó.
Hầu tước Lorne

18

Có thể thay đổi PWD, sử dụng JNA / JNI để thực hiện các cuộc gọi đến libc. Các anh chàng JRuby có một thư viện java tiện dụng để thực hiện các cuộc gọi POSIX được gọi là jna-posix Đây là thông tin maven

Bạn có thể xem một ví dụ về việc sử dụng nó ở đây (mã Clojure, xin lỗi). Nhìn vào chức năng chdirToRoot


1
Dường như không có phiên bản hiện đại của jna-posix. Tôi đã rẽ nhánh và thêm một: github.com/pbiggar/jnr-poseix . Tôi có thể xác nhận rằng tôi có thể thay đổi NKT với điều này.
Paul Biggar

Đúng. Xin lỗi, tôi đã cấu trúc lại tệp đó và quên câu trả lời này được liên kết với tệp. Đã sửa.
Allen Rohner

Bạn có thể làm điều này, nhưng nếu bạn cũng không thay đổi thuộc tính user.dirhệ thống thì File.getAbsolutePath()sẽ giải quyết user.dir, trong khi tên đường dẫn trong Tệp sẽ giải quyết theo thư mục làm việc của HĐH.
Morrie

Liên quan đến câu hỏi ban đầu, sử dụng jnr-posix, làm cách nào tôi có thể thay đổi thư mục làm việc hiện tại trong Java. Lớp nào tôi phải tạo một thể hiện để sử dụng phương thức chdir? Tôi đã không thực sự hiểu ví dụ Clojure đưa ra. Cảm ơn trước.
Sam Saint-Pettersen

12

Như đã đề cập, bạn không thể thay đổi CWD của JVM nhưng nếu bạn khởi chạy một quy trình khác bằng Runtime.exec (), bạn có thể sử dụng phương thức quá tải cho phép bạn chỉ định thư mục làm việc. Điều này thực sự không phải để chạy chương trình Java của bạn trong một thư mục khác, nhưng trong nhiều trường hợp khi người ta cần khởi chạy một chương trình khác như tập lệnh Perl, bạn có thể chỉ định thư mục làm việc của tập lệnh đó trong khi vẫn giữ nguyên thư mục làm việc của JVM.

Xem javadocs Runtime.exec

Đặc biệt,

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

nơi dirlà thư mục làm việc để chạy các tiến trình con trong


1
Đây dường như là một câu trả lời tốt hơn câu trả lời được chấp nhận (bắt đầu bằng "Không có cách nào đáng tin cậy để làm điều này trong Java thuần túy."). Có cách nào để kiến ​​nghị cho câu trả lời này được coi là câu trả lời được chấp nhận không?
John

11

Nếu tôi hiểu chính xác, một chương trình Java bắt đầu bằng một bản sao của các biến môi trường hiện tại. Mọi thay đổi thông qua System.setProperty(String, String)là sửa đổi bản sao, không phải các biến môi trường ban đầu. Không phải điều này cung cấp một lý do kỹ lưỡng về lý do tại sao Sun chọn hành vi này, nhưng có lẽ nó làm sáng tỏ một chút ...


2
Bạn dường như đang trộn lẫn các biến môi trường và thuộc tính. Cái trước được kế thừa từ HĐH trong khi cái sau có thể được định nghĩa trên dòng lệnh bằng cách sử dụng -D. Nhưng tôi đồng ý, trên JVM bắt đầu các thuộc tính được xác định trước như user.dirđược sao chép từ HĐH và thay đổi chúng sau này không giúp ích gì.
maaartinus

Việc thay đổi user.dirảnh hưởng File.getAbsolutePath()File.getCanonicalPath(), nhưng không phải ý tưởng của hệ điều hành về thư mục làm việc, điều này quyết định cách giải quyết tên đường dẫn tệp khi truy cập tệp.
Morrie

5

Thư mục làm việc là một tính năng của hệ điều hành (được đặt khi quá trình bắt đầu). Tại sao bạn không chuyển thuộc tính Hệ thống của riêng bạn ( -Dsomeprop=/my/path) và sử dụng mã đó trong mã của bạn làm cha mẹ của Tệp của bạn:

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

Bởi vì đoạn mã chứa đường dẫn tệp được mã hóa cứng và có thể sử dụng hàm tạo được mã hóa cứng không chỉ định thư mục cha để làm việc. Atleast, đó là tình huống tôi có :)
David Mann

4

Điều thông minh hơn / dễ dàng hơn để làm ở đây là chỉ cần thay đổi mã của bạn để thay vì mở tệp giả sử rằng nó tồn tại trong thư mục làm việc hiện tại (tôi giả sử bạn đang làm một cái gì đó như thế new File("blah.txt"), chỉ cần tự xây dựng đường dẫn đến tệp.

Để người dùng chuyển vào thư mục cơ sở, đọc nó từ tệp cấu hình, quay lại user.dirnếu không tìm thấy các thuộc tính khác, v.v. Nhưng việc cải thiện logic trong chương trình của bạn sẽ dễ dàng hơn nhiều so với thay đổi cách biến môi trường làm việc.


2

Tôi đã cố gắng để gọi

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

Nó dường như làm việc. Nhưng

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

ném một FileNotFoundExceptionmặc dù

myFile.getAbsolutePath()

hiển thị đường dẫn chính xác. Tôi đã đọc điều này . Tôi nghĩ vấn đề là:

  • Java biết thư mục hiện tại với cài đặt mới.
  • Nhưng việc xử lý tập tin được thực hiện bởi hệ thống hoạt động. Thật không may, thư mục hiện tại mới được thiết lập.

Giải pháp có thể là:

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

Nó tạo ra một đối tượng tệp là một đối tượng tuyệt đối với thư mục hiện tại được JVM biết. Nhưng mã đó phải tồn tại trong một lớp được sử dụng, nó cần thay đổi mã được sử dụng lại.

~~~ ~ JcHartmut


"Nó tạo ra một đối tượng tập tin" - có. Nhưng nó không tạo ra một tập tin trong hệ thống tập tin. Bạn đã quên sử dụng file.createNewFile () sau đó.
Gangnus

2

Bạn có thể dùng

Tệp mới ("tương đối / đường dẫn"). getAbsoluteFile ()

sau

System.setProperty ("user.dir", "/ some / thư mục")

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

Sẽ in

C:\OtherProject\data\data.csv

1
Xin lưu ý rằng hành vi này đã thay đổi trong Java 11, xem bug.openjdk.java.net/browse/JDK-8202127 . Họ không khuyên bạn nên sử dụngSystem.setProperty("user.dir", "/some/directory")
Martin

0

Câu trả lời khác có thể cho câu hỏi này có thể phụ thuộc vào lý do bạn mở tệp. Đây có phải là tệp thuộc tính hoặc tệp có cấu hình liên quan đến ứng dụng của bạn không?

Nếu đây là trường hợp bạn có thể xem xét thử tải tệp qua trình tải lớp, theo cách này bạn có thể tải bất kỳ tệp nào Java có quyền truy cập.


0

Nếu bạn chạy các lệnh của mình trong shell, bạn có thể viết một cái gì đó như "java -cp" và thêm bất kỳ thư mục nào bạn muốn phân tách bằng ":" nếu java không tìm thấy thứ gì trong một thư mục, nó sẽ đi thử và tìm chúng trong các thư mục khác, đó là là những gì tôi làm


0

Bạn có thể thay đổi thư mục làm việc thực tế của quy trình bằng JNI hoặc JNA.

Với JNI, bạn có thể sử dụng các hàm riêng để đặt thư mục. Phương pháp POSIX là chdir(). Trên Windows, bạn có thể sử dụng SetCurrentDirectory().

Với JNA, bạn có thể gói các hàm riêng trong các ràng buộc Java.

Cho cửa sổ:

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

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

Đối với hệ thống POSIX:

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

    /** int chdir(const char *path); */
    int chdir( String path );
}

-1

Sử dụng 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);

nhập javax.swing.filechooser.FileSystemView;
Borneq

1
Câu trả lời này không liên quan đến câu hỏi.
Aaron Digulla
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.