190

How can I change the current working directory from within a Java program? Everything I've been able to find about the issue claims that you simply can't do it, but I can't believe that that's really the case.

I have a piece of code that opens a file using a hard-coded relative file path from the directory it's normally started in, and I just want to be able to use that code from within a different Java program without having to start it from within a particular directory. It seems like you should just be able to call System.setProperty( "user.dir", "/path/to/dir" ), but as far as I can figure out, calling that line just silently fails and does nothing.

I would understand if Java didn't allow you to do this, if it weren't for the fact that it allows you to get the current working directory, and even allows you to open files using relative file paths....

Nick
  • 2,411
  • 3
  • 19
  • 12
  • 1
    Getting and using information is different from changing it. For example on Windows you can easily get environment variables but it is harder to change them (in system wide way). – PhiLho May 08 '09 at 14:58
  • 1
    https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4045688 states in the evaluation section "Since that time, no further customers have come forward or were otherwise identified. ..." and as of 2018 we've got some 175.000 views of this question :-( – Wolfgang Fahl Sep 16 '18 at 08:27

14 Answers14

161

There is no reliable way to do this in pure Java. Setting the user.dir property via System.setProperty() or java -Duser.dir=... does seem to affect subsequent creations of Files, but not e.g. FileOutputStreams.

The File(String parent, String child) constructor can help if you build up your directory path separately from your file path, allowing easier swapping.

An alternative is to set up a script to run Java from a different directory, or use JNI native code as suggested below.

The relevant OpenJDK bug was closed in 2008 as "will not fix".

Edric
  • 24,639
  • 13
  • 81
  • 91
Michael Myers
  • 188,989
  • 46
  • 291
  • 292
  • Thanks. I had been able to find some other sources saying you couldn't do it, but I wasn't convinced until I saw that bug you linked to. Unfortunately it's not code I can easily modify, so I'll probably end up using something along the lines of the ".bat" solution you described. – Nick May 08 '09 at 15:36
  • 22
    i don't think i've found a single difference between java and c# that makes me think, "those java guys sure know what they're doing" – Jake Feb 22 '12 at 21:05
  • 1
    you can't do this in _pure_ Java. You _can_ do it with JNI. – Alnitak May 04 '12 at 10:26
  • 3
    Hard to believe that java doesn't have some parameter "start in this directory..." at least... – rogerdpack Jan 31 '13 at 23:30
  • 10
    In Java's defense (and I'm a UNIX guy that desires this feature)...it is a VM that is meant to be agnostic to OS particulars. The "present working directory" idiom is not available in some operating systems. – Tony K. Dec 20 '13 at 04:56
  • 14
    To be fair to Java, they had to do it first, C# has had the benefit of being able to learn from their mistakes in a lot of areas. – Ryan Leach Jan 21 '14 at 05:20
  • That's wrong. See http://www.javacodex.com/Files/Set-The-Current-Working-Directory . Works at least on windows and linux. – Volker Seibt Jun 09 '15 at 13:44
  • @VolkerSeibt: [The answer below](https://stackoverflow.com/a/13981910/13531) suggests that that only works with `getAbsoluteFile` or `getAbsolutePath`, but I just tried it and it seems you are correct. – Michael Myers Jun 09 '15 at 16:19
  • 3
    @VolkerSeibt: On further investigation, it seems that user.dir only works for some classes, including the one I tested with at first. `new FileOutputStream("foo.txt").close();` creates the file in the original working directory even if user.dir is changed by the program. – Michael Myers Jun 09 '15 at 17:13
44

If you run your legacy program with ProcessBuilder, you will be able to specify its working directory.

PhiLho
  • 40,535
  • 6
  • 96
  • 134
  • 1
    's route is the route I took. I was able to run an executable from a different working directory with the following: File WorkingDir = new File("C:\\path\\to\\working\\dir\\"); ProcessBuilder pBuilder = new ProcessBuilder("C:\\path\\to\\working\\dir\\executable.exe"); pBuilder.directory(WorkingDir); Process p = pBuilder.start(); – CatsAndCode Feb 09 '18 at 13:11
29

There is a way to do this using the system property "user.dir". The key part to understand is that getAbsoluteFile() must be called (as shown below) or else relative paths will be resolved against the default "user.dir" value.

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
  • 643
  • 6
  • 8
  • 5
    But it doesn't change the current working directory. Only the value of `user.dir`. The fact that the absolute path becomes critical proves it. – user207421 Sep 20 '17 at 06:13
23

It is possible to change the PWD, using JNA/JNI to make calls to libc. The JRuby guys have a handy java library for making POSIX calls called jnr-posix. Here's the maven info

Flow
  • 23,572
  • 15
  • 99
  • 156
Allen Rohner
  • 1,108
  • 1
  • 10
  • 16
  • 2
    There doesn't seem to be a modern version of jna-posix. I forked and added one: http://github.com/pbiggar/jnr-posix. I can confirm that I can change the PWD with this. – Paul Biggar Nov 25 '11 at 18:10
  • Yes. Sorry, I refactored that file and forgot this answer linked to the file. Fixed. – Allen Rohner Feb 01 '13 at 03:55
  • 2
    You can do this, but if you don't also change the `user.dir` system property then `File.getAbsolutePath()` will resolve against `user.dir`, while the pathname in File resolves against the OS working directory. – Morrie Nov 10 '14 at 14:25
  • In relation to the original question, using jnr-posix, how can I change the current working directory in Java. What class do I have to create a instance of to use the chdir method? I did not really understand the Clojure example given. Thanks in advance. – Sam Saint-Pettersen Nov 26 '14 at 15:09
20

As mentioned you can't change the CWD of the JVM but if you were to launch another process using Runtime.exec() you can use the overloaded method that lets you specify the working directory. This is not really for running your Java program in another directory but for many cases when one needs to launch another program like a Perl script for example, you can specify the working directory of that script while leaving the working dir of the JVM unchanged.

See Runtime.exec javadocs

Specifically,

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

where dir is the working directory to run the subprocess in

Bizmarck
  • 2,663
  • 2
  • 33
  • 48
  • 2
    This seems to be a better answer than the accepted answer (which starts with "There is no reliable way to do this in pure Java. "). Is there a way to petition for this answer to be considered as the accepted answer? – John Aug 31 '15 at 20:06
10

If I understand correctly, a Java program starts with a copy of the current environment variables. Any changes via System.setProperty(String, String) are modifying the copy, not the original environment variables. Not that this provides a thorough reason as to why Sun chose this behavior, but perhaps it sheds a little light...

Adam Paynter
  • 46,244
  • 33
  • 149
  • 164
  • 4
    You seem to be mixing up environment variables and properties. The former gets inherited from the OS while the latter can be defined on the command line using `-D`. But I agree, on JVM start predefined properties like `user.dir` get copied from the OS and changing them later doesn't help. – maaartinus Apr 25 '12 at 15:39
  • 1
    Changing `user.dir` affects `File.getAbsolutePath()` and `File.getCanonicalPath()`, but not the OS's idea of the working directory, which dictates how File pathnames are resolved when accessing files. – Morrie Nov 10 '14 at 14:28
6

The working directory is a operating system feature (set when the process starts). Why don't you just pass your own System property (-Dsomeprop=/my/path) and use that in your code as the parent of your File:

File f = new File ( System.getProperty("someprop"), myFilename)
raphaëλ
  • 6,393
  • 2
  • 29
  • 35
  • Because the piece of code contains a hard coded file path, and probably uses a hard-coded constructor that does not specify the parent directory to work from. Atleast, thats the situation I have :) – David Mann Mar 05 '14 at 16:24
4

The smarter/easier thing to do here is to just change your code so that instead of opening the file assuming that it exists in the current working directory (I assume you are doing something like new File("blah.txt"), just build the path to the file yourself.

Let the user pass in the base directory, read it from a config file, fall back to user.dir if the other properties can't be found, etc. But it's a whole lot easier to improve the logic in your program than it is to change how environment variables work.

matt b
  • 138,234
  • 66
  • 282
  • 345
2

I have tried to invoke

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

It seems to work. But

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

throws a FileNotFoundException though

myFile.getAbsolutePath()

shows the correct path. I have read this. I think the problem is:

  • Java knows the current directory with the new setting.
  • But the file handling is done by the operation system. It does not know the new set current directory, unfortunately.

The solution may be:

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

It creates a file Object as absolute one with the current directory which is known by the JVM. But that code should be existing in a used class, it needs changing of reused codes.

~~~~JcHartmut

Benjamin Trent
  • 7,378
  • 3
  • 31
  • 41
  • "It creates a file Object" - yes. But it does not create a file in the filesystem. You forgot to use file.createNewFile() after that. – Gangnus May 19 '19 at 12:37
2

You can use

new File("relative/path").getAbsoluteFile()

after

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

Will print

C:\OtherProject\data\data.csv
javacommons
  • 113
  • 5
  • 1
    Please note that this behavior changed in Java 11, see https://bugs.openjdk.java.net/browse/JDK-8202127. They do not recommend using `System.setProperty("user.dir", "/some/directory")` – Martin Dec 17 '19 at 10:05
1

You can change the process's actual working directory using JNI or JNA.

With JNI, you can use native functions to set the directory. The POSIX method is chdir(). On Windows, you can use SetCurrentDirectory().

With JNA, you can wrap the native functions in Java binders.

For Windows:

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

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

For POSIX systems:

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

    /** int chdir(const char *path); */
    int chdir( String path );
}
Andy Thomas
  • 84,978
  • 11
  • 107
  • 151
0

If you run your commands in a shell you can write something like "java -cp" and add any directories you want separated by ":" if java doesnt find something in one directory it will go try and find them in the other directories, that is what I do.

lesolorzanov
  • 3,536
  • 8
  • 35
  • 53
0

The other possible answer to this question may depend on the reason you are opening the file. Is this a property file or a file that has some configuration related to your application?

If this is the case you may consider trying to load the file through the classpath loader, this way you can load any file Java has access to.

Nathan Feger
  • 19,122
  • 11
  • 62
  • 71
-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
  • 261
  • 1
  • 6
  • 13