Cambiare la directory di lavoro corrente in Java?


169

Come posso cambiare l'attuale directory di lavoro da un programma Java? Tutto ciò che sono stato in grado di trovare sul problema afferma che semplicemente non puoi farlo, ma non posso credere che sia davvero così.

Ho un pezzo di codice che apre un file usando un percorso di file relativo codificato dalla directory in cui è normalmente avviato, e voglio solo essere in grado di usare quel codice da un altro programma Java senza doverlo avviare dall'interno una directory particolare. Sembra che dovresti essere in grado di chiamare System.setProperty( "user.dir", "/path/to/dir" ), ma per quanto posso capire, chiamare quella linea fallisce silenziosamente e non fa nulla.

Vorrei capire se Java non ti ha permesso di fare questo, se non fosse per il fatto che ti consente di ottenere la directory di lavoro corrente e ti consente anche di aprire i file usando i percorsi dei file relativi ....


1
Ottenere e utilizzare le informazioni è diverso dal modificarle. Ad esempio su Windows è possibile ottenere facilmente variabili di ambiente, ma è più difficile modificarle (a livello di sistema).
PhiLho,

1
bugs.java.com/bugdatabase/view_bug.do?bug_id=4045688 afferma nella sezione di valutazione "Da quel momento, nessun altro cliente si è fatto avanti o è stato altrimenti identificato ..." e dal 2018 ne abbiamo circa 175.000 punti di vista di questa domanda :-(
Wolfgang Fahl,

Risposte:


146

Non esiste un modo affidabile per farlo in Java puro. L'impostazione della user.dirproprietà tramite System.setProperty()o java -Duser.dir=...sembra influire sulle creazioni successive di Files, ma non ad es FileOutputStreams.

Il File(String parent, String child)costruttore può essere utile se si crea il percorso della directory separatamente dal percorso del file, consentendo uno scambio più semplice.

Un'alternativa è configurare uno script per eseguire Java da una directory diversa o utilizzare il codice nativo JNI come suggerito di seguito .

Il relativo bug Sun è stato chiuso nel 2008 come "non risolverà".


12
non credo di aver trovato una sola differenza tra java e c # che mi faccia pensare, "quei ragazzi di java sicuramente sanno cosa stanno facendo"
Jake,

2
Difficile credere che java non abbia alcun parametro "start in this directory ..." almeno ...
rogerdpack

5
A difesa di Java (e io sono un tipo UNIX che desidera questa funzionalità) ... è una VM pensata per essere agnostica rispetto ai dettagli del sistema operativo. Il linguaggio "presente directory di lavoro" non è disponibile in alcuni sistemi operativi.
Tony K.

4
Per essere onesti con Java, hanno dovuto prima farlo, C # ha avuto il vantaggio di poter imparare dai propri errori in molte aree.
Ryan The Leach,

3
@VolkerSeibt: su ulteriori indagini, sembra che user.dir funzioni solo per alcune classi, inclusa quella con cui ho provato all'inizio. new FileOutputStream("foo.txt").close();crea il file nella directory di lavoro originale anche se user.dir viene modificato dal programma.
Michael Myers

37

Se esegui il tuo programma legacy con ProcessBuilder , sarai in grado di specificare la sua directory di lavoro .


1
Il percorso è il percorso che ho seguito. Sono stato in grado di eseguire un eseguibile da una directory di lavoro diversa con il seguente: File WorkingDir = new File ("C: \\ path \\ to \\ working \\ dir \\"); ProcessBuilder pBuilder = new ProcessBuilder ("C: \\ path \\ to \\ working \\ dir \\ eseguable.exe"); pBuilder.directory (WorkingDir); Processo p = pBuilder.start ();
CatsAndCode

29

V'è un modo per farlo utilizzando la proprietà di sistema "user.dir". La parte fondamentale da capire è che getAbsoluteFile () deve essere chiamato (come mostrato di seguito) altrimenti i percorsi relativi verranno risolti rispetto al valore "user.dir" predefinito .

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
il percorso assoluto è fondamentale
HackNone

5
Ma non cambia la directory di lavoro corrente. Solo il valore di user.dir. Il fatto che il percorso assoluto diventi critico lo dimostra.
Marchese di Lorne,

18

È possibile modificare il PWD, usando JNA / JNI per effettuare chiamate a libc. I ragazzi di JRuby hanno una comoda libreria java per effettuare chiamate POSIX chiamate jna-posix Ecco le informazioni di Maven

Puoi vedere un esempio del suo utilizzo qui (codice Clojure, scusa). Guarda la funzione chdirToRoot


1
Non sembra esserci una versione moderna di jna-posix. Ho biforcato e aggiunto uno: github.com/pbiggar/jnr-posix . Posso confermare che posso cambiare il PWD con questo.
Paul Biggar,

Sì. Siamo spiacenti, ho riformattato quel file e ho dimenticato questa risposta collegata al file. Fisso.
Allen Rohner,

Puoi farlo, ma se non modifichi anche la user.dirproprietà di sistema, File.getAbsolutePath()risolverà il problema user.dir, mentre il percorso in File si risolve nella directory di lavoro del sistema operativo.
Morrie,

In relazione alla domanda originale, usando jnr-posix, come posso cambiare l'attuale directory di lavoro in Java. Di quale classe devo creare un'istanza per usare il metodo chdir? Non ho davvero capito l'esempio di Clojure dato. Grazie in anticipo.
Sam Saint-Pettersen,

12

Come accennato, non è possibile modificare il CWD della JVM ma se si dovesse avviare un altro processo utilizzando Runtime.exec () è possibile utilizzare il metodo sovraccarico che consente di specificare la directory di lavoro. Questo non è proprio per l'esecuzione del programma Java in un'altra directory, ma per molti casi quando è necessario avviare un altro programma come uno script Perl, ad esempio, è possibile specificare la directory di lavoro di quello script lasciando invariata la directory di lavoro della JVM.

Vedi Runtime.exec javadocs

In particolare,

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

dove si dirtrova la directory di lavoro in cui eseguire il sottoprocesso


1
Questa sembra essere una risposta migliore della risposta accettata (che inizia con "Non esiste un modo affidabile per farlo in Java puro"). C'è un modo per presentare una petizione affinché questa risposta sia considerata come la risposta accettata?
Giovanni,

11

Se ho capito bene, un programma Java inizia con una copia delle variabili di ambiente correnti. Eventuali modifiche tramite System.setProperty(String, String)stanno modificando la copia, non le variabili di ambiente originali. Non che ciò fornisca una ragione completa per cui Sun abbia scelto questo comportamento, ma forse fa luce un po '...


2
Sembra che tu stia mescolando variabili e proprietà di ambiente. Il primo viene ereditato dal sistema operativo mentre il secondo può essere definito sulla riga di comando utilizzando -D. Ma sono d'accordo, su JVM avviare proprietà predefinite come user.diressere copiate dal sistema operativo e modificarle in seguito non aiuta.
maaartinus,

La modifica user.dirinfluisce File.getAbsolutePath()e File.getCanonicalPath(), ma non l'idea del sistema operativo della directory di lavoro, che determina come i percorsi dei file vengono risolti quando si accede ai file.
Morrie,

5

La directory di lavoro è una funzionalità del sistema operativo (impostata all'avvio del processo). Perché non passi semplicemente la tua proprietà di sistema ( -Dsomeprop=/my/path) e la usi nel tuo codice come genitore del tuo file:

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

Perché il pezzo di codice contiene un percorso di file con codice hard e probabilmente utilizza un costruttore con codice hard che non specifica la directory padre da cui lavorare. Almeno, questa è la situazione che ho :)
David Mann,

4

La cosa più intelligente / più facile da fare qui è semplicemente cambiare il codice in modo che invece di aprire il file supponendo che esista nella directory di lavoro corrente (suppongo che stai facendo qualcosa di simile new File("blah.txt"), costruisci tu stesso il percorso del file.

Consenti all'utente di passare nella directory di base, di leggerlo da un file di configurazione, di tornare indietro user.dirse non è possibile trovare le altre proprietà, ecc. Ma è molto più semplice migliorare la logica del tuo programma piuttosto che cambiare come le variabili d'ambiente funzionano.


2

Ho provato a invocare

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

Sembra funzionare Ma

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

genera un FileNotFoundExceptionpensiero

myFile.getAbsolutePath()

mostra il percorso corretto. Ho letto questo . Penso che il problema sia:

  • Java conosce la directory corrente con la nuova impostazione.
  • Ma la gestione dei file viene eseguita dal sistema operativo. Purtroppo non conosce la nuova directory corrente impostata.

La soluzione può essere:

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

Crea un oggetto file come assoluto con la directory corrente conosciuta dalla JVM. Ma quel codice dovrebbe esistere in una classe usata, ha bisogno di cambiare i codici riutilizzati.

~~~~ JcHartmut


"Crea un oggetto file" - sì. Ma non crea un file nel filesystem. Dopo di che hai dimenticato di usare file.createNewFile ().
Gangnus,

2

Puoi usare

nuovo file ("relative / path"). getAbsoluteFile ()

dopo

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

Stamperà

C:\OtherProject\data\data.csv

1
Si noti che questo comportamento è cambiato in Java 11, consultare bugs.openjdk.java.net/browse/JDK-8202127 . Si sconsiglia di utilizzareSystem.setProperty("user.dir", "/some/directory")
Martin il

0

L'altra possibile risposta a questa domanda potrebbe dipendere dal motivo per cui si sta aprendo il file. Si tratta di un file delle proprietà o di un file con una configurazione relativa all'applicazione?

In tal caso, potresti provare a caricare il file tramite il caricatore del percorso di classe, in questo modo puoi caricare qualsiasi file a cui Java abbia accesso.


0

Se esegui i tuoi comandi in una shell puoi scrivere qualcosa come "java -cp" e aggiungere tutte le directory che desideri siano separate da ":" se java non trova qualcosa in una directory, proverà a trovarle nelle altre directory, che è quello che faccio.


0

È possibile modificare la directory di lavoro effettiva del processo utilizzando JNI o ​​JNA.

Con JNI, è possibile utilizzare le funzioni native per impostare la directory. Il metodo POSIX è chdir(). Su Windows, puoi usare SetCurrentDirectory().

Con JNA, puoi racchiudere le funzioni native nei raccoglitori Java.

Per Windows:

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

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

Per i sistemi POSIX:

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

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

-1

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

import javax.swing.filechooser.FileSystemView;
Borneq,

1
Questa risposta non è correlata alla domanda.
Aaron Digulla,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.