¿Cambiar el directorio de trabajo actual en Java?

169

¿Cómo puedo cambiar el directorio de trabajo actual desde un programa Java? Todo lo que he podido encontrar sobre el problema afirma que simplemente no puede hacerlo, pero no puedo creer que ese sea realmente el caso.

Tengo un código que abre un archivo usando una ruta de archivo relativa codificada desde el directorio en el que normalmente se inició, y solo quiero poder usar ese código desde un programa Java diferente sin tener que iniciarlo desde dentro Un directorio particular. Parece que debería poder llamar System.setProperty( "user.dir", "/path/to/dir" ), pero por lo que puedo entender, llamar a esa línea simplemente falla en silencio y no hace nada.

Entendería si Java no le permitiera hacer esto, si no fuera por el hecho de que le permite obtener el directorio de trabajo actual, e incluso le permite abrir archivos utilizando rutas de archivos relativas ...

Mella
fuente
1
Obtener y usar información es diferente de cambiarla. Por ejemplo, en Windows puede obtener fácilmente variables de entorno, pero es más difícil cambiarlas (en todo el sistema).
PhiLho
1
bugs.java.com/bugdatabase/view_bug.do?bug_id=4045688 declara en la sección de evaluación "Desde ese momento, no se han presentado más clientes o fueron identificados de otra manera ..." y a partir de 2018 tenemos unos 175,000 puntos de vista de esta pregunta :-(
Wolfgang Fahl

Respuestas:

146

No hay una forma confiable de hacer esto en Java puro. Establecer la user.dirpropiedad a través de System.setProperty()o java -Duser.dir=...parece afectar las creaciones posteriores de Files, pero no por ejemplo FileOutputStreams.

los File(String parent, String child) constructor puede ayudar si construye su ruta de directorio por separado de su ruta de archivo, lo que permite un intercambio más fácil.

Una alternativa es configurar un script para ejecutar Java desde un directorio diferente, o usar el código nativo JNI como se sugiere a continuación .

El error Sun relevante se cerró en 2008 como "no se solucionará".

Michael Myers
fuente
12
no creo haber encontrado una sola diferencia entre java y c # que me haga pensar, "esos tipos de java saben lo que están haciendo"
Jake
2
Es difícil de creer que Java no tenga algún parámetro "comenzar en este directorio ..." al menos ...
rogerdpack
55
En defensa de Java (y yo soy un tipo de UNIX que desea esta característica) ... es una máquina virtual que debe ser independiente de los detalles del sistema operativo. La expresión "directorio de trabajo actual" no está disponible en algunos sistemas operativos.
Tony K.
44
Para ser justos con Java, primero tenían que hacerlo, C # ha tenido el beneficio de poder aprender de sus errores en muchas áreas.
Ryan The Leach
3
@VolkerSeibt: En una investigación más profunda, parece que user.dir solo funciona para algunas clases, incluida la que probé al principio. new FileOutputStream("foo.txt").close();crea el archivo en el directorio de trabajo original incluso si user.dir es cambiado por el programa.
Michael Myers
37

Si ejecuta su programa heredado con ProcessBuilder , podrá especificar su directorio de trabajo .

PhiLho
fuente
1
La ruta es la ruta que tomé. Pude ejecutar un ejecutable desde un directorio de trabajo diferente con lo siguiente: File WorkingDir = new File ("C: \\ path \\ to \\ working \\ dir \\"); ProcessBuilder pBuilder = nuevo ProcessBuilder ("C: \\ ruta \\ to \\ working \\ dir \\ executetable.exe"); pBuilder.directory (WorkingDir); Proceso p = pBuilder.start ();
CatsAndCode
29

No es una forma de hacer esto utilizando la propiedad del sistema "user.dir". La parte clave a entender es que se debe llamar a getAbsoluteFile () (como se muestra a continuación) o de lo contrario las rutas relativas se resolverán con el valor predeterminado "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
fuente
3
la ruta absoluta es crítica
HackNone
55
Pero no cambia el directorio de trabajo actual. Solo el valor de user.dir. El hecho de que el camino absoluto se vuelva crítico lo demuestra.
Marqués de Lorne
18

Es posible cambiar el PWD, utilizando JNA / JNI para hacer llamadas a libc. Los chicos de JRuby tienen una práctica biblioteca java para hacer llamadas POSIX llamada jna-posix Aquí está la información de Maven

Puede ver un ejemplo de su uso aquí (código Clojure, lo siento). Mira la función chdirToRoot

Allen Rohner
fuente
1
No parece haber una versión moderna de jna-posix. Bifurqué y agregué uno: github.com/pbiggar/jnr-posix . Puedo confirmar que puedo cambiar la PWD con esto.
Paul Biggar
Si. Lo sentimos, modifiqué ese archivo y olvidé esta respuesta vinculada al archivo. Fijo.
Allen Rohner
Puede hacerlo, pero si no cambia también la user.dirpropiedad del sistema, File.getAbsolutePath()se resolverá en contra user.dir, mientras que la ruta de acceso en Archivo se resuelve en el directorio de trabajo del sistema operativo.
Morrie
En relación con la pregunta original, usando jnr-posix, ¿cómo puedo cambiar el directorio de trabajo actual en Java? ¿De qué clase tengo que crear una instancia para usar el método chdir? Realmente no entendí el ejemplo de Clojure dado. Gracias por adelantado.
Sam Saint-Pettersen
12

Como se mencionó, no puede cambiar el CWD de la JVM, pero si iniciara otro proceso usando Runtime.exec (), puede usar el método sobrecargado que le permite especificar el directorio de trabajo. Esto no es realmente para ejecutar su programa Java en otro directorio, pero en muchos casos cuando uno necesita iniciar otro programa como un script Perl, por ejemplo, puede especificar el directorio de trabajo de ese script mientras deja el directorio de trabajo de la JVM sin cambios.

Ver Runtime.exec javadocs

Específicamente,

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

¿Dónde direstá el directorio de trabajo para ejecutar el subproceso?

Bizmarck
fuente
1
Esta parece ser una mejor respuesta que la respuesta aceptada (que comienza con "No hay una forma confiable de hacer esto en Java puro"). ¿Hay alguna forma de solicitar que esta respuesta se considere como la respuesta aceptada?
John
11

Si entiendo correctamente, un programa Java comienza con una copia de las variables de entorno actuales. Cualquier cambio mediante System.setProperty(String, String)está modificando la copia, no las variables de entorno originales. No es que esto proporcione una razón exhaustiva de por qué Sun eligió este comportamiento, pero tal vez arroja un poco de luz ...

Adam Paynter
fuente
2
Parece que estás mezclando variables de entorno y propiedades. El primero se hereda del sistema operativo, mientras que el segundo se puede definir en la línea de comando utilizando -D. Pero estoy de acuerdo, en JVM iniciar propiedades predefinidas como user.dirser copiado del sistema operativo y cambiarlas más tarde no ayuda.
maaartinus
El cambio user.dirafecta File.getAbsolutePath()y File.getCanonicalPath(), pero no la idea del SO del directorio de trabajo, que dicta cómo se resuelven los nombres de ruta de los archivos al acceder a los archivos.
Morrie
5

El directorio de trabajo es una característica del sistema operativo (se establece cuando se inicia el proceso). ¿Por qué no simplemente pasa su propia propiedad del Sistema ( -Dsomeprop=/my/path) y la usa en su código como padre de su Archivo:

File f = new File ( System.getProperty("someprop"), myFilename)
raphaëλ
fuente
Debido a que el fragmento de código contiene una ruta de archivo codificada, y probablemente utiliza un constructor codificado que no especifica el directorio principal desde el cual trabajar. Al menos, esa es la situación que tengo :)
David Mann
4

Lo más inteligente / fácil de hacer aquí es cambiar su código para que, en lugar de abrir el archivo, suponga que existe en el directorio de trabajo actual (supongo que está haciendo algo así new File("blah.txt"), simplemente cree la ruta del archivo usted mismo.

Deje que el usuario pase al directorio base, léalo desde un archivo de configuración, recurra a user.dirsi no se pueden encontrar las otras propiedades, etc. Pero es mucho más fácil mejorar la lógica en su programa que cambiar cómo Las variables de entorno funcionan.

mate b
fuente
2

He intentado invocar

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

Parece funcionar. Pero

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

lanza un FileNotFoundExceptionpensamiento

myFile.getAbsolutePath()

Muestra la ruta correcta. He leído este . Creo que el problema es:

  • Java conoce el directorio actual con la nueva configuración.
  • Pero el manejo del archivo lo realiza el sistema operativo. Desgraciadamente, no conoce el nuevo directorio actual establecido.

La solución puede ser:

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

Crea un Objeto de archivo como absoluto con el directorio actual que es conocido por la JVM. Pero ese código debe existir en una clase usada, necesita cambiar los códigos reutilizados.

~~~~ JcHartmut

Hartmut Schorrig
fuente
"Crea un Objeto de archivo" - sí. Pero no crea un archivo en el sistema de archivos. Olvidó usar file.createNewFile () después de eso.
Gangnus
2

Puedes usar

Archivo nuevo ("relativo / ruta"). getAbsoluteFile ()

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

Imprimirá

C:\OtherProject\data\data.csv
javacommons
fuente
1
Tenga en cuenta que este comportamiento cambió en Java 11, consulte bugs.openjdk.java.net/browse/JDK-8202127 . No recomiendan usarSystem.setProperty("user.dir", "/some/directory")
Martin
0

La otra posible respuesta a esta pregunta puede depender de la razón por la que está abriendo el archivo. ¿Es este un archivo de propiedades o un archivo que tiene alguna configuración relacionada con su aplicación?

Si este es el caso, puede considerar intentar cargar el archivo a través del cargador de classpath, de esta manera puede cargar cualquier archivo al que Java tenga acceso.

Nathan Feger
fuente
0

Si ejecuta sus comandos en un shell, puede escribir algo como "java -cp" y agregar los directorios que desee separados por ":" si java no encuentra algo en un directorio, intentará encontrarlos en los otros directorios, eso es lo que hago

lesolorzanov
fuente
0

Puede cambiar el directorio de trabajo real del proceso utilizando JNI o ​​JNA.

Con JNI, puede usar funciones nativas para configurar el directorio. El método POSIX es chdir(). En Windows, puedes usar SetCurrentDirectory().

Con JNA, puede ajustar las funciones nativas en carpetas de Java.

Para ventanas:

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

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

Para sistemas 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
fuente
-1

Use 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
fuente
import javax.swing.filechooser.FileSystemView;
Borneq
1
Esta respuesta no está relacionada con la pregunta.
Aaron Digulla