Das aktuelle Arbeitsverzeichnis in Java ändern?

169

Wie kann ich das aktuelle Arbeitsverzeichnis in einem Java-Programm ändern? Alles, was ich über das Problem herausfinden konnte, besagt, dass man es einfach nicht kann, aber ich kann nicht glauben, dass das wirklich der Fall ist.

Ich habe einen Code, der eine Datei unter Verwendung eines fest codierten relativen Dateipfads aus dem Verzeichnis öffnet, in dem sie normalerweise gestartet wurde, und ich möchte diesen Code nur in einem anderen Java-Programm verwenden können, ohne ihn von innen starten zu müssen ein bestimmtes Verzeichnis. Es scheint, als ob Sie nur anrufen können sollten System.setProperty( "user.dir", "/path/to/dir" ), aber soweit ich herausfinden kann, schlägt das Anrufen dieser Leitung nur stillschweigend fehl und führt zu nichts.

Ich würde verstehen , wenn Java nicht erlauben Ihnen , dies zu tun, wenn es nicht für die Tatsache, dass es Ihnen erlaubt , zu erhalten das aktuelle Arbeitsverzeichnis, und ermöglicht es Ihnen auch zum Öffnen von Dateien relativen Dateipfaden verwenden ....

Nick
quelle
1
Das Abrufen und Verwenden von Informationen unterscheidet sich vom Ändern. Unter Windows können Sie beispielsweise leicht Umgebungsvariablen abrufen, diese sind jedoch schwieriger zu ändern (systemweit).
PhiLho
1
bugs.java.com/bugdatabase/view_bug.do?bug_id=4045688 gibt im Bewertungsabschnitt "Seitdem haben sich keine weiteren Kunden gemeldet oder wurden anderweitig identifiziert. ..." an. Ab 2018 haben wir rund 175.000 Ansichten dieser Frage :-(
Wolfgang Fahl

Antworten:

146

In reinem Java gibt es keine zuverlässige Möglichkeit, dies zu tun. Das Festlegen der user.dirEigenschaft über System.setProperty()oder java -Duser.dir=...scheint nachfolgende Kreationen von zu beeinflussen Files, jedoch nicht z FileOutputStreams.

Der File(String parent, String child)Konstruktor kann helfen, wenn Sie Ihren Verzeichnispfad getrennt von Ihrem Dateipfad erstellen, um das Austauschen zu vereinfachen.

Eine Alternative besteht darin, ein Skript zum Ausführen von Java aus einem anderen Verzeichnis einzurichten oder nativen JNI-Code wie unten vorgeschlagen zu verwenden .

Der relevante Sun-Fehler wurde 2008 geschlossen, da "nicht behoben werden kann".

Michael Myers
quelle
12
Ich glaube nicht, dass ich einen einzigen Unterschied zwischen Java und C # gefunden habe, der mich denken lässt: "Diese Java-Leute wissen genau, was sie tun"
Jake
2
Kaum zu glauben, dass Java keinen Parameter "Start in diesem Verzeichnis ..." hat, zumindest ...
Rogerdpack
5
Zu Javas Verteidigung (und ich bin ein UNIX-Typ, der diese Funktion wünscht) ... ist es eine VM, die unabhängig von Betriebssystemangaben sein soll. Das Idiom "Aktuelles Arbeitsverzeichnis" ist in einigen Betriebssystemen nicht verfügbar.
Tony K.
4
Um Java gegenüber fair zu sein, mussten sie es zuerst tun. C # hatte den Vorteil, in vielen Bereichen aus ihren Fehlern lernen zu können.
Ryan The Leach
3
@VolkerSeibt: Bei weiteren Untersuchungen scheint user.dir nur für einige Klassen zu funktionieren, einschließlich derjenigen, mit denen ich zuerst getestet habe. new FileOutputStream("foo.txt").close();Erstellt die Datei im ursprünglichen Arbeitsverzeichnis, auch wenn user.dir vom Programm geändert wird.
Michael Myers
37

Wenn Sie Ihr Legacy - Programm mit laufen Process , werden Sie in der Lage sein , seine Arbeit geben Verzeichnis .

PhiLho
quelle
1
Die Route ist die Route, die ich genommen habe. Ich konnte eine ausführbare Datei aus einem anderen Arbeitsverzeichnis wie folgt ausführen: Datei WorkingDir = neue Datei ("C: \\ Pfad \\ zu \\ Arbeitsverzeichnis \\"); ProcessBuilder pBuilder = neuer ProcessBuilder ("C: \\ Pfad \\ zu \\ Arbeit \\ Verzeichnis \\ ausführbare Datei.exe"); pBuilder.directory (WorkingDir); Prozess p = pBuilder.start ();
CatsAndCode
29

Es gibt eine Möglichkeit, dies mit der Systemeigenschaft "user.dir" zu tun. Der Schlüssel zum Verständnis ist, dass getAbsoluteFile () aufgerufen werden muss (wie unten gezeigt), sonst werden relative Pfade gegen den Standardwert "user.dir" aufgelöst.

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.
quelle
3
absoluter Pfad ist kritisch
HackNone
5
Das aktuelle Arbeitsverzeichnis wird jedoch nicht geändert. Nur der Wert von user.dir. Die Tatsache, dass der absolute Pfad kritisch wird, beweist es.
Marquis von Lorne
18

Es ist möglich, die PWD mithilfe von JNA / JNI zu ändern, um libc aufzurufen. Die JRuby-Leute haben eine praktische Java-Bibliothek für POSIX-Aufrufe namens jna-posix. Hier sind die Maven-Informationen

Ein Beispiel für die Verwendung finden Sie hier (Clojure-Code, sorry). Schauen Sie sich die Funktion chdirToRoot an

Allen Rohner
quelle
1
Es scheint keine moderne Version von jna-posix zu geben. Ich gabelte und fügte eine hinzu: github.com/pbiggar/jnr-posix . Ich kann bestätigen, dass ich damit die PWD ändern kann.
Paul Biggar
Ja. Entschuldigung, ich habe diese Datei überarbeitet und diese mit der Datei verknüpfte Antwort vergessen. Fest.
Allen Rohner
Sie können dies tun, aber wenn Sie nicht auch die user.dirSystemeigenschaft ändern , File.getAbsolutePath()wird gegen aufgelöst user.dir, während der Pfadname in Datei gegen das Arbeitsverzeichnis des Betriebssystems aufgelöst wird.
Morrie
Wie kann ich in Bezug auf die ursprüngliche Frage mit jnr-posix das aktuelle Arbeitsverzeichnis in Java ändern? Für welche Klasse muss ich eine Instanz erstellen, um die chdir-Methode verwenden zu können? Ich habe das gegebene Clojure-Beispiel nicht wirklich verstanden. Danke im Voraus.
Sam Saint-Pettersen
12

Wie bereits erwähnt, können Sie die CWD der JVM nicht ändern. Wenn Sie jedoch einen anderen Prozess mit Runtime.exec () starten, können Sie die überladene Methode verwenden, mit der Sie das Arbeitsverzeichnis angeben können. Dies ist nicht wirklich zum Ausführen Ihres Java-Programms in einem anderen Verzeichnis gedacht. In vielen Fällen, in denen ein anderes Programm wie beispielsweise ein Perl-Skript gestartet werden muss, können Sie das Arbeitsverzeichnis dieses Skripts angeben, während das Arbeitsverzeichnis der JVM unverändert bleibt.

Siehe Runtime.exec javadocs

Speziell,

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

Wo dirbefindet sich das Arbeitsverzeichnis, in dem der Unterprozess ausgeführt werden soll?

Bizmarck
quelle
1
Dies scheint eine bessere Antwort zu sein als die akzeptierte Antwort (die mit "Es gibt keinen zuverlässigen Weg, dies in reinem Java zu tun." Beginnt). Gibt es eine Möglichkeit, eine Petition einzureichen, damit diese Antwort als akzeptierte Antwort betrachtet wird?
John
11

Wenn ich das richtig verstehe, beginnt ein Java-Programm mit einer Kopie der aktuellen Umgebungsvariablen. Alle Änderungen über ändern System.setProperty(String, String)die Kopie, nicht die ursprünglichen Umgebungsvariablen. Nicht, dass dies einen gründlichen Grund dafür liefert, warum Sun dieses Verhalten gewählt hat, aber vielleicht wirft es ein wenig Licht ...

Adam Paynter
quelle
2
Sie scheinen Umgebungsvariablen und Eigenschaften zu verwechseln. Ersteres wird vom Betriebssystem geerbt, während letzteres in der Befehlszeile mit definiert werden kann -D. Aber ich bin damit einverstanden, dass beim Start von JVM vordefinierte Eigenschaften wie user.dirdas Kopieren vom Betriebssystem und das spätere Ändern nicht helfen.
Maaartinus
Das Ändern user.dirwirkt sich auf File.getAbsolutePath()und File.getCanonicalPath()nicht auf die Vorstellung des Betriebssystems vom Arbeitsverzeichnis aus, die vorschreibt, wie Dateipfadnamen beim Zugriff auf Dateien aufgelöst werden.
Morrie
5

Das Arbeitsverzeichnis ist eine Betriebssystemfunktion (festgelegt, wenn der Prozess gestartet wird). Warum übergeben Sie nicht einfach Ihre eigene Systemeigenschaft ( -Dsomeprop=/my/path) und verwenden diese in Ihrem Code als übergeordnetes Element Ihrer Datei:

File f = new File ( System.getProperty("someprop"), myFilename)
raphaëλ
quelle
Weil der Code einen fest codierten Dateipfad enthält und wahrscheinlich einen fest codierten Konstruktor verwendet, der nicht das übergeordnete Verzeichnis angibt, aus dem gearbeitet werden soll. Zumindest ist das die Situation, die ich habe :)
David Mann
4

Das klügere / einfachere, was Sie hier tun können, ist, einfach Ihren Code so zu ändern, dass anstatt die Datei zu öffnen, vorausgesetzt, dass sie im aktuellen Arbeitsverzeichnis vorhanden ist (ich gehe davon aus, dass Sie so etwas tun new File("blah.txt"), erstellen Sie einfach den Pfad zur Datei selbst.

Lassen Sie den Benutzer das Basisverzeichnis übergeben, lesen Sie es aus einer Konfigurationsdatei, greifen Sie darauf zurück, user.dirwenn die anderen Eigenschaften nicht gefunden werden können usw. Es ist jedoch viel einfacher, die Logik in Ihrem Programm zu verbessern, als zu ändern, wie Umgebungsvariablen funktionieren.

matt b
quelle
2

Ich habe versucht aufzurufen

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

Es scheint zu funktionieren. Aber

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

wirft einen FileNotFoundExceptionobwohl

myFile.getAbsolutePath()

zeigt den richtigen Pfad. Ich habe diese Zeilen lesen . Ich denke das Problem ist:

  • Java kennt das aktuelle Verzeichnis mit der neuen Einstellung.
  • Die Dateiverwaltung erfolgt jedoch durch das Betriebssystem. Das neu eingestellte aktuelle Verzeichnis ist leider nicht bekannt.

Die Lösung kann sein:

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

Es wird ein Dateiobjekt als absolutes mit dem aktuellen Verzeichnis erstellt, das der JVM bekannt ist. Dieser Code sollte jedoch in einer verwendeten Klasse vorhanden sein und muss wiederverwendete Codes ändern.

~~~~ JcHartmut

Hartmut Schorrig
quelle
"Es wird ein Dateiobjekt erstellt" - ja. Es wird jedoch keine Datei im Dateisystem erstellt. Sie haben danach vergessen, file.createNewFile () zu verwenden.
Gangnus
2

Sie können verwenden

neue Datei ("relative / path"). getAbsoluteFile ()

nach dem

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

Wird gedruckt

C:\OtherProject\data\data.csv
Javacommons
quelle
1
Bitte beachten Sie, dass sich dieses Verhalten in Java 11 geändert hat, siehe bugs.openjdk.java.net/browse/JDK-8202127 . Sie empfehlen nicht zu verwendenSystem.setProperty("user.dir", "/some/directory")
Martin
0

Die andere mögliche Antwort auf diese Frage hängt möglicherweise vom Grund ab, aus dem Sie die Datei öffnen. Handelt es sich um eine Eigenschaftendatei oder um eine Datei, deren Konfiguration sich auf Ihre Anwendung bezieht?

Wenn dies der Fall ist, können Sie versuchen, die Datei über den Klassenpfadlader zu laden. Auf diese Weise können Sie jede Datei laden, auf die Java Zugriff hat.

Nathan Feger
quelle
0

Wenn Sie Ihre Befehle in einer Shell ausführen, können Sie etwas wie "java -cp" schreiben und alle Verzeichnisse hinzufügen, die durch ":" getrennt werden sollen. Wenn Java etwas in einem Verzeichnis nicht findet, versucht es, es in den anderen Verzeichnissen zu finden ist was ich tue.

lesolorzanov
quelle
0

Sie können das tatsächliche Arbeitsverzeichnis des Prozesses mithilfe von JNI oder JNA ändern.

Mit JNI können Sie native Funktionen verwenden, um das Verzeichnis festzulegen . Die POSIX-Methode ist chdir(). Unter Windows können Sie verwenden SetCurrentDirectory().

Mit JNA können Sie die nativen Funktionen in Java-Ordner einbinden.

Für Windows:

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

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

Für POSIX-Systeme:

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

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

Verwenden Sie 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
quelle
import javax.swing.filechooser.FileSystemView;
Borneq
1
Diese Antwort bezieht sich nicht auf die Frage.
Aaron Digulla