Changer le répertoire de travail actuel en Java?

169

Comment puis-je modifier le répertoire de travail actuel à partir d'un programme Java? Tout ce que j'ai pu trouver sur le problème prétend que vous ne pouvez tout simplement pas le faire, mais je ne peux pas croire que ce soit vraiment le cas.

J'ai un morceau de code qui ouvre un fichier en utilisant un chemin de fichier relatif codé en dur à partir du répertoire dans lequel il est normalement démarré, et je veux juste pouvoir utiliser ce code à partir d'un autre programme Java sans avoir à le démarrer de l'intérieur un répertoire particulier. Il semble que vous devriez simplement pouvoir appeler System.setProperty( "user.dir", "/path/to/dir" ), mais pour autant que je sache, appeler cette ligne échoue silencieusement et ne fait rien.

Je comprendrais si Java ne vous permettait pas de le faire, si ce n'était du fait qu'il vous permet d' obtenir le répertoire de travail actuel, et vous permet même d'ouvrir des fichiers en utilisant des chemins de fichiers relatifs ....

pseudo
la source
1
Obtenir et utiliser des informations est différent de les modifier. Par exemple, sous Windows, vous pouvez facilement obtenir des variables d'environnement, mais il est plus difficile de les modifier (à l'échelle du système).
PhiLho
1
bugs.java.com/bugdatabase/view_bug.do?bug_id=4045688 indique dans la section d'évaluation "Depuis lors, aucun autre client ne s'est manifesté ou n'a été autrement identifié. ..." et en 2018, nous en avons 175.000 vues de cette question :-(
Wolfgang Fahl

Réponses:

146

Il n'existe aucun moyen fiable de le faire en Java pur. La définition de la user.dirpropriété via System.setProperty()ou java -Duser.dir=...semble affecter les créations ultérieures de Files, mais pas par exemple FileOutputStreams.

le File(String parent, String child) constructeur peut vous aider si vous créez votre chemin de répertoire séparément de votre chemin de fichier, ce qui permet un échange plus facile.

Une alternative consiste à configurer un script pour exécuter Java à partir d'un répertoire différent ou à utiliser du code natif JNI comme suggéré ci-dessous .

Le bogue Sun pertinent a été fermé en 2008 car "ne résoudra pas".

Michael Myers
la source
12
je ne pense pas avoir trouvé une seule différence entre java et c # qui me fasse penser, "ces gars de Java savent vraiment ce qu'ils font"
Jake
2
Difficile de croire que java n'a pas de paramètre "démarrer dans ce répertoire ..." au moins ...
rogerdpack
5
Dans la défense de Java (et je suis un gars UNIX qui désire cette fonctionnalité) ... c'est une VM qui est censée être indépendante des particularités du système d'exploitation. L'idiome «répertoire de travail actuel» n'est pas disponible dans certains systèmes d'exploitation.
Tony K.
4
Pour être juste envers Java, ils devaient d'abord le faire, C # a eu l'avantage de pouvoir apprendre de leurs erreurs dans de nombreux domaines.
Ryan The Leach
3
@VolkerSeibt: Après une enquête plus approfondie, il semble que user.dir ne fonctionne que pour certaines classes, y compris celle avec laquelle j'ai testé au début. new FileOutputStream("foo.txt").close();crée le fichier dans le répertoire de travail d'origine même si user.dir est modifié par le programme.
Michael Myers
37

Si vous exécutez votre ancien programme avec ProcessBuilder , vous pourrez spécifier son répertoire de travail .

PhiLho
la source
1
La route de est la route que j'ai empruntée. J'ai pu exécuter un exécutable à partir d'un répertoire de travail différent avec ce qui suit: File WorkingDir = new File ("C: \\ path \\ to \\ working \\ dir \\"); ProcessBuilder pBuilder = nouveau ProcessBuilder ("C: \\ chemin \\ vers \\ working \\ dir \\ executable.exe"); pBuilder.directory (WorkingDir); Processus p = pBuilder.start ();
CatsAndCode
29

Il existe un moyen de le faire en utilisant la propriété système "user.dir". L'élément clé à comprendre est que getAbsoluteFile () doit être appelé (comme indiqué ci-dessous), sinon les chemins relatifs seront résolus par rapport à la valeur par défaut "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");
    }
}
Steve K
la source
3
le chemin absolu est critique
HackNone
5
Mais cela ne change pas le répertoire de travail actuel. Seule la valeur de user.dir. Le fait que le chemin absolu devienne critique le prouve.
Marquis of Lorne
18

Il est possible de changer le PWD, en utilisant JNA / JNI pour faire des appels à la libc. Les gars de JRuby ont une bibliothèque java pratique pour faire des appels POSIX appelée jna-posix Voici les infos maven

Vous pouvez voir un exemple de son utilisation ici (code Clojure, désolé). Regardez la fonction chdirToRoot

Allen Rohner
la source
1
Il ne semble pas y avoir de version moderne de jna-posix. J'ai bifurqué et ajouté un: github.com/pbiggar/jnr-posix . Je peux confirmer que je peux changer le PWD avec cela.
Paul Biggar
Oui. Désolé, j'ai refactoré ce fichier et oublié cette réponse liée au fichier. Fixé.
Allen Rohner
Vous pouvez le faire, mais si vous ne modifiez pas également la user.dirpropriété système, la File.getAbsolutePath()résolution sera contre user.dir, tandis que le chemin d'accès dans File est résolu par rapport au répertoire de travail du système d'exploitation.
Morrie
En ce qui concerne la question d'origine, en utilisant jnr-posix, comment puis-je changer le répertoire de travail actuel en Java. De quelle classe dois-je créer une instance pour utiliser la méthode chdir? Je n'ai pas vraiment compris l'exemple de Clojure donné. Merci d'avance.
Sam Saint-Pettersen
12

Comme mentionné, vous ne pouvez pas modifier le CWD de la JVM, mais si vous lancez un autre processus à l'aide de Runtime.exec (), vous pouvez utiliser la méthode surchargée qui vous permet de spécifier le répertoire de travail. Ce n'est pas vraiment pour exécuter votre programme Java dans un autre répertoire mais dans de nombreux cas quand on a besoin de lancer un autre programme comme un script Perl par exemple, vous pouvez spécifier le répertoire de travail de ce script tout en laissant le répertoire de travail de la JVM inchangé.

Voir les javadocs Runtime.exec

Plus précisément,

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

direst le répertoire de travail dans lequel exécuter le sous-processus

Bizmarck
la source
1
Cela semble être une meilleure réponse que la réponse acceptée (qui commence par "Il n'y a pas de moyen fiable de faire cela en Java pur."). Existe-t-il un moyen de pétition pour que cette réponse soit considérée comme la réponse acceptée?
John
11

Si je comprends bien, un programme Java démarre avec une copie des variables d'environnement actuelles. Tous les changements via System.setProperty(String, String)modifient la copie, pas les variables d'environnement d'origine. Non pas que cela fournisse une raison approfondie pour laquelle Sun a choisi ce comportement, mais peut-être que cela jette un peu de lumière ...

Adam Paynter
la source
2
Vous semblez mélanger les variables d'environnement et les propriétés. Le premier est hérité du système d'exploitation tandis que le second peut être défini sur la ligne de commande en utilisant -D. Mais je suis d'accord, sur JVM, démarrez des propriétés prédéfinies telles que user.dirse faire copier à partir du système d'exploitation et les modifier plus tard n'aide pas.
maaartinus
Le changement user.diraffecte File.getAbsolutePath()et File.getCanonicalPath(), mais pas l'idée du système d'exploitation du répertoire de travail, qui dicte la façon dont les chemins de fichiers sont résolus lors de l'accès aux fichiers.
Morrie
5

Le répertoire de travail est une fonctionnalité du système d'exploitation (définie au démarrage du processus). Pourquoi ne pas simplement transmettre votre propre propriété System ( -Dsomeprop=/my/path) et l'utiliser dans votre code en tant que parent de votre fichier:

File f = new File ( System.getProperty("someprop"), myFilename)
raphaëλ
la source
Parce que le morceau de code contient un chemin de fichier codé en dur et utilise probablement un constructeur codé en dur qui ne spécifie pas le répertoire parent à partir duquel travailler. Au moins, c'est la situation que j'ai :)
David Mann
4

La chose la plus intelligente / la plus simple à faire ici est de simplement changer votre code afin qu'au lieu d'ouvrir le fichier en supposant qu'il existe dans le répertoire de travail actuel (je suppose que vous faites quelque chose comme new File("blah.txt") , construisez simplement le chemin du fichier vous-même.

Laissez l'utilisateur passer dans le répertoire de base, le lire à partir d'un fichier de configuration, revenir user.dirsi les autres propriétés ne peuvent pas être trouvées, etc. Mais il est beaucoup plus facile d'améliorer la logique de votre programme que de changer la façon dont les variables d'environnement fonctionnent.

mat b
la source
2

J'ai essayé d'invoquer

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

Cela semble fonctionner. Mais

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

jette une FileNotFoundExceptionpensée

myFile.getAbsolutePath()

montre le chemin correct. J'ai lu ceci . Je pense que le problème est:

  • Java connaît le répertoire actuel avec le nouveau paramètre.
  • Mais la gestion des fichiers est effectuée par le système d'exploitation. Il ne connaît malheureusement pas le nouveau répertoire courant de l'ensemble.

La solution peut être:

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

Il crée un fichier Object comme absolu avec le répertoire courant qui est connu par la JVM. Mais ce code doit exister dans une classe utilisée, il doit changer les codes réutilisés.

~~~~ JcHartmut

Hartmut Schorrig
la source
"Il crée un objet de fichier" - oui. Mais cela ne crée pas de fichier dans le système de fichiers. Vous avez oublié d'utiliser file.createNewFile () après cela.
Gangnus
2

Vous pouvez utiliser

nouveau fichier ("relatif / chemin"). getAbsoluteFile ()

après

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

Imprimera

C:\OtherProject\data\data.csv
javacommons
la source
1
Veuillez noter que ce comportement a changé dans Java 11, voir bugs.openjdk.java.net/browse/JDK-8202127 . Ils ne recommandent pas d'utiliserSystem.setProperty("user.dir", "/some/directory")
Martin
0

L'autre réponse possible à cette question peut dépendre de la raison pour laquelle vous ouvrez le fichier. S'agit-il d'un fichier de propriétés ou d'un fichier dont la configuration est liée à votre application?

Si tel est le cas, vous pouvez essayer de charger le fichier via le chargeur de chemin de classe, de cette façon, vous pouvez charger n'importe quel fichier auquel Java a accès.

Nathan Feger
la source
0

Si vous exécutez vos commandes dans un shell, vous pouvez écrire quelque chose comme "java -cp" et ajouter tous les répertoires que vous voulez séparés par ":" si java ne trouve pas quelque chose dans un répertoire, il essaiera de les trouver dans les autres répertoires, cela c'est ce que je fais.

lesolorzanov
la source
0

Vous pouvez modifier le répertoire de travail réel du processus à l'aide de JNI ou JNA.

Avec JNI, vous pouvez utiliser des fonctions natives pour définir le répertoire. La méthode POSIX est chdir(). Sous Windows, vous pouvez utiliserSetCurrentDirectory() .

Avec JNA, vous pouvez encapsuler les fonctions natives dans des classeurs Java.

Pour les fenêtres:

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

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

Pour les systèmes 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
la source
-1

Utiliser 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
la source
import javax.swing.filechooser.FileSystemView;
Borneq du
1
Cette réponse n'est pas liée à la question.
Aaron Digulla