เปลี่ยนไดเรกทอรีทำงานปัจจุบันใน 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 บางส่วน มุมมองของคำถามนี้ :-(
Wolfgang Fahl

คำตอบ:


146

ไม่มีวิธีที่เชื่อถือได้ในการทำเช่นนี้ในจาวาบริสุทธิ์ การตั้งค่าuser.dirสถานที่ให้บริการผ่านSystem.setProperty()หรือjava -Duser.dir=...ไม่ดูเหมือนจะส่งผลกระทบต่อการสร้างสรรค์ที่ตามมาของแต่ไม่ได้เช่นFilesFileOutputStreams

ตัวFile(String parent, String child)สร้างสามารถช่วยถ้าคุณสร้างเส้นทางไดเรกทอรีของคุณแยกต่างหากจากเส้นทางไฟล์ของคุณช่วยให้การแลกเปลี่ยนง่ายขึ้น

ทางเลือกคือการตั้งค่าสคริปต์เพื่อเรียกใช้ Java จากไดเรกทอรีที่แตกต่างกันหรือใช้ JNI รหัสพื้นเมืองตามข้อเสนอแนะดังต่อไปนี้

ข้อผิดพลาด Sun ที่เกี่ยวข้องถูกปิดในปี 2551 เนื่องจาก "จะไม่แก้ไข"


12
ฉันไม่คิดว่าฉันได้พบความแตกต่างเดียวระหว่าง Java และ C # ที่ทำให้ผมคิดว่า "ผู้คนจาวาแน่ใจว่ารู้ว่าสิ่งที่พวกเขากำลังทำ"
เจค

2
ยากที่จะเชื่อว่า java ไม่มีพารามิเตอร์ "เริ่มต้นในไดเรกทอรีนี้ ... " อย่างน้อย ...
rogerdpack

5
ในการป้องกันของ Java (และฉันเป็นคนที่ใช้ระบบปฏิบัติการยูนิกซ์ที่ต้องการคุณสมบัตินี้) ... มันเป็น VM ที่มีความหมายที่จะไม่เชื่อในรายละเอียดของระบบปฏิบัติการ สำนวน "ไดเรกทอรีทำงานปัจจุบัน" ไม่พร้อมใช้งานในบางระบบปฏิบัติการ
Tony K.

4
เพื่อความเป็นธรรมกับ Java พวกเขาต้องทำก่อน C # ได้รับประโยชน์จากความสามารถในการเรียนรู้จากความผิดพลาดของพวกเขาในหลายพื้นที่
Ryan The Leach

3
@VolkerSeibt: ในการตรวจสอบเพิ่มเติมดูเหมือนว่า user.dir ใช้ได้กับบางคลาสเท่านั้นรวมถึงคลาสที่ฉันทดสอบด้วยในตอนแรก new FileOutputStream("foo.txt").close();สร้างไฟล์ในไดเรกทอรีการทำงานดั้งเดิมแม้ว่า user.dir จะถูกเปลี่ยนโดยโปรแกรม
Michael Myers

37

หากคุณเรียกใช้โปรแกรมดั้งเดิมด้วยProcessBuilderคุณจะสามารถระบุไดเรกทอรีทำงานได้


1
เส้นทางของฉันคือเส้นทางที่ฉันไป ฉันสามารถเรียกใช้ไฟล์ที่เรียกทำงานได้จากไดเรคทอรีการทำงานที่แตกต่างกันดังต่อไปนี้ File WorkingDir = ไฟล์ใหม่ ("C: \\ path \\ to \\ working \\ dir \\"); ProcessBuilder pBuilder = ProcessBuilder ใหม่ ("C: \\ เส้นทาง \\ ไปที่ \\ ทำงาน \\ dir \\ executable.exe"); pBuilder.directory (WorkingDir); กระบวนการ p = pBuilder.start ();
CatsAndCode

29

มีเป็นวิธีการทำเช่นนี้โดยใช้สถานที่ให้บริการระบบ "user.dir" a ส่วนสำคัญที่ต้องเข้าใจคือต้องเรียกใช้ 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เฉพาะค่าของ ความจริงที่ว่าเส้นทางที่แน่นอนกลายเป็นสิ่งสำคัญที่จะพิสูจน์มัน
มาร์ควิสแห่ง Lorne

18

เป็นไปได้ที่จะเปลี่ยน PWD โดยใช้ JNA / JNI เพื่อโทรไปยัง libc พวก JRuby มีห้องสมุด java ที่มีประโยชน์สำหรับการโทรแบบ POSIX ชื่อjna-posixนี่คือข้อมูล maven

คุณสามารถดูตัวอย่างการใช้งานได้ที่นี่ (รหัส Clojure ขออภัย) ดูฟังก์ชัน chdirToRoot


1
ดูเหมือนจะไม่เป็น jna-posix รุ่นทันสมัย ฉันคดเคี้ยวและเพิ่มอีกหนึ่ง: github.com/pbiggar/jnr-posix ฉันยืนยันได้ว่าฉันสามารถเปลี่ยน PWD ได้ด้วยสิ่งนี้
Paul Biggar

ใช่. ขออภัยฉันสร้างไฟล์อีกครั้งและลืมคำตอบที่เชื่อมโยงกับไฟล์นี้ แก้ไขแล้ว.
Allen Rohner

คุณสามารถทำได้ แต่ถ้าคุณยังไม่เปลี่ยนuser.dirคุณสมบัติของระบบก็File.getAbsolutePath()จะแก้ไขuser.dirในขณะที่ชื่อพา ธ ในไฟล์แก้ไขกับไดเรกทอรีการทำงานของระบบปฏิบัติการ
Morrie

ในความสัมพันธ์กับคำถามเดิมโดยใช้ jnr-posix ฉันจะเปลี่ยนไดเรกทอรีการทำงานปัจจุบันใน Java ได้อย่างไร ฉันต้องใช้คลาสใดในการสร้างอินสแตนซ์ของการใช้วิธี chdir ฉันไม่เข้าใจตัวอย่าง Clojure ที่ระบุ ขอบคุณล่วงหน้า.
Sam Saint-Pettersen

12

ดังที่ได้กล่าวมาแล้วคุณไม่สามารถเปลี่ยน CWD ของ JVM ได้ แต่หากคุณต้องการเปิดใช้กระบวนการอื่นโดยใช้ Runtime.exec () คุณสามารถใช้วิธีโอเวอร์โหลดที่ช่วยให้คุณระบุไดเรกทอรีทำงานได้ นี่ไม่ใช่การรันโปรแกรม Java ของคุณในไดเรกทอรีอื่น แต่ในหลายกรณีเมื่อผู้ใช้ต้องการเรียกใช้โปรแกรมอื่นเช่นสคริปต์ Perl คุณสามารถระบุไดเร็กตอรี่การทำงานของสคริปต์นั้นได้ในขณะที่ยังคงไม่เปลี่ยนแปลง JVM

ดูที่Runtime.exec javadocs

โดยเฉพาะอย่างยิ่ง

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

โดยที่dirเป็นไดเร็คทอรี่สำหรับใช้ในการเรียกใช้กระบวนการย่อย


1
นี่น่าจะเป็นคำตอบที่ดีกว่าคำตอบที่ยอมรับได้ (ซึ่งเริ่มต้นด้วย "ไม่มีวิธีที่เชื่อถือได้ในการใช้จาวาบริสุทธิ์") มีวิธียื่นคำร้องสำหรับคำตอบนี้เพื่อพิจารณาว่าเป็นคำตอบที่ยอมรับได้หรือไม่?
จอห์น

11

หากฉันเข้าใจถูกต้องโปรแกรม Java จะเริ่มต้นด้วยสำเนาของตัวแปรสภาพแวดล้อมปัจจุบัน การเปลี่ยนแปลงใด ๆ ที่ทำผ่านSystem.setProperty(String, String)การแก้ไขสำเนาไม่ใช่ตัวแปรสภาพแวดล้อมดั้งเดิม ไม่ใช่ว่านี่เป็นเหตุผลอย่างละเอียดว่าทำไมซันเลือกลักษณะการทำงานนี้ แต่บางทีมันอาจทำให้บางสิ่งเล็กน้อย ...


2
คุณดูเหมือนจะผสมตัวแปรสภาพแวดล้อมและคุณสมบัติ -Dอดีตได้รับการสืบทอดมาจากระบบปฏิบัติการในขณะที่หลังสามารถกำหนดในบรรทัดคำสั่งโดยใช้ แต่ฉันเห็นด้วยใน JVM เริ่มต้นคุณสมบัติที่กำหนดไว้ล่วงหน้าเช่นการuser.dirคัดลอกจากระบบปฏิบัติการและการเปลี่ยนแปลงในภายหลังไม่ได้ช่วย
maaartinus

การเปลี่ยนแปลงuser.dirส่งผลกระทบต่อFile.getAbsolutePath()และFile.getCanonicalPath()ไม่ใช่แนวคิดของ OS ที่ทำงานกับไดเรกทอรีซึ่งกำหนดว่าชื่อพา ธ ของไฟล์จะได้รับการแก้ไขอย่างไรเมื่อเข้าถึงไฟล์
Morrie

5

ไดเรกทอรีทำงานเป็นคุณสมบัติของระบบปฏิบัติการ (ตั้งค่าเมื่อกระบวนการเริ่มต้น) ทำไมคุณไม่เพียงแค่ส่งคุณสมบัติระบบของคุณเอง ( -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");

มันสร้างไฟล์ Object เป็นสัมบูรณ์ที่มีไดเร็กทอรีปัจจุบันซึ่ง JVM รู้จัก แต่รหัสนั้นควรมีอยู่ในคลาสที่ใช้มันต้องการการเปลี่ยนรหัสที่นำกลับมาใช้ใหม่

~~~~ JcHartmut


"มันสร้างวัตถุไฟล์" - ใช่ แต่จะไม่สร้างไฟล์ในระบบไฟล์ คุณลืมใช้ file.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")
Martin

0

คำตอบอื่น ๆ ที่เป็นไปได้สำหรับคำถามนี้อาจขึ้นอยู่กับเหตุผลที่คุณเปิดไฟล์ นี่เป็นไฟล์คุณสมบัติหรือไฟล์ที่มีการกำหนดค่าบางอย่างที่เกี่ยวข้องกับแอปพลิเคชันของคุณหรือไม่?

หากเป็นกรณีนี้คุณอาจลองโหลดไฟล์ผ่าน classpath loader ด้วยวิธีนี้คุณสามารถโหลดไฟล์ใด ๆ ที่ 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;
Borneq

1
คำตอบนี้ไม่เกี่ยวข้องกับคำถาม
Aaron Digulla
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.