Change the current working directory in C++
Asked Answered
M

9

64

How can I change my current working directory in C++ in a platform-agnostic way?

I found the direct.h header file, which is Windows compatible, and the unistd.h, which is UNIX/POSIX compatible.

Murage answered 14/8, 2010 at 21:20 Comment(2)
@noɥʇʎPʎzɐɹC So the standard committee has established a standard required way to change the working directory, circa C++17, via filesystem. pepper_chico's answer already denotes that. filesystem is currently available in g++5.3 and Visual Studio 2015 as an optional include. If that is the environment that you're working in I can write you an answer using #ifdef to make filesystem's access cross platform?Chiekochien
@JonathanMee if it is good enough, I may do a multiple bountyEcbatana
R
53

The chdir function works on both POSIX (manpage) and Windows (called _chdir there but an alias chdir exists).

Both implementations return zero on success and -1 on error. As you can see in the manpage, more distinguished errno values are possible in the POSIX variant, but that shouldn't really make a difference for most use cases.

Residual answered 14/8, 2010 at 21:33 Comment(8)
I'm asking since Visual Studio wants me to use direct.h, but when I try building the same code in Linux, it crashes on my head, saying that I need to use unistd.hMurage
@sparkFinder, you will usually need to include different headers on different platforms when dealing with nonstandard functions such as chdir(). IIRC, GCC will define _WIN32 when targeting Windows, so you could use that with #include to choose a header.Pleurodynia
@sparkFinder: You can check for Visual Studio with #ifdef _MSC_VER and then include the direct.h header. If it's not defined, use unistd.h. This should be enough as the other major programming environment on Windows, MinGW, has the unistd header.Residual
You could just declare the prototype yourself.Blockade
chdir on windows is deprecated.Ecbatana
@noɥʇʎPʎzɐɹC Nothing about it being deprecated on this page. What's your source?Yovonnda
@Yovonnda _chdir != chdir _chdir is not cross platform while chdir is deprecated.Chiekochien
This answer is not correct as it promotes the use of a deprecated functionality as stated here msdn.microsoft.com/en-us/library/ms235420.aspxSynoptic
W
65

Now, with C++17 is possible to use std::filesystem::current_path:

#include <filesystem>
int main() {
    auto path = std::filesystem::current_path(); //getting path
    std::filesystem::current_path(path); //setting path
}
Wreak answered 18/7, 2019 at 14:14 Comment(2)
This changes only the current process's path. The operating shell's current path is not changed.Accrete
@MarcheRemi Yes, that's typically what's meant when you want to change the current working directory. Under most OSs, it's not possible to change any other process' working directory at all.Spatial
R
53

The chdir function works on both POSIX (manpage) and Windows (called _chdir there but an alias chdir exists).

Both implementations return zero on success and -1 on error. As you can see in the manpage, more distinguished errno values are possible in the POSIX variant, but that shouldn't really make a difference for most use cases.

Residual answered 14/8, 2010 at 21:33 Comment(8)
I'm asking since Visual Studio wants me to use direct.h, but when I try building the same code in Linux, it crashes on my head, saying that I need to use unistd.hMurage
@sparkFinder, you will usually need to include different headers on different platforms when dealing with nonstandard functions such as chdir(). IIRC, GCC will define _WIN32 when targeting Windows, so you could use that with #include to choose a header.Pleurodynia
@sparkFinder: You can check for Visual Studio with #ifdef _MSC_VER and then include the direct.h header. If it's not defined, use unistd.h. This should be enough as the other major programming environment on Windows, MinGW, has the unistd header.Residual
You could just declare the prototype yourself.Blockade
chdir on windows is deprecated.Ecbatana
@noɥʇʎPʎzɐɹC Nothing about it being deprecated on this page. What's your source?Yovonnda
@Yovonnda _chdir != chdir _chdir is not cross platform while chdir is deprecated.Chiekochien
This answer is not correct as it promotes the use of a deprecated functionality as stated here msdn.microsoft.com/en-us/library/ms235420.aspxSynoptic
A
21

For C++, boost::filesystem::current_path (setter and getter prototypes).

A file system library based on Boost.Filesystem will be added to the standard.

Algebra answered 5/3, 2013 at 3:59 Comment(0)
T
20

This cross-platform sample code for changing the working directory using POSIX chdir and MS _chdir as recommend in this answer. Likewise for determining the current working directory, the analogous getcwd and _getcwd are used.

These platform differences are hidden behind the macros cd and cwd.

As per the documentation, chdir's signature is int chdir(const char *path) where path is absolute or relative. chdir will return 0 on success. getcwd is slightly more complicated because it needs (in one variant) a buffer to store the fetched path in as seen in char *getcwd(char *buf, size_t size). It returns NULL on failure and a pointer to the same passed buffer on success. The code sample makes use of this returned char pointer directly.

The sample is based on @MarcD's but corrects a memory leak. Additionally, I strove for concision, no dependencies, and only basic failure/error checking as well as ensuring it works on multiple (common) platforms.

I tested it on OSX 10.11.6, Centos7, and Win10. For OSX & Centos, I used g++ changedir.cpp -o changedir to build and ran as ./changedir <path>.

On Win10, I built with cl.exe changedir.cpp /EHsc /nologo.

MVP solution

$ cat changedir.cpp

#ifdef _WIN32
#include <direct.h>
// MSDN recommends against using getcwd & chdir names
#define cwd _getcwd
#define cd _chdir
#else
#include "unistd.h"
#define cwd getcwd
#define cd chdir
#endif

#include <iostream>

char buf[4096]; // never know how much is needed

int main(int argc , char** argv) {

  if (argc > 1) {
    std::cout  << "CWD: " << cwd(buf, sizeof buf) << std::endl;

    // Change working directory and test for success
    if (0 == cd(argv[1])) {
      std::cout << "CWD changed to: " << cwd(buf, sizeof buf) << std::endl;
    }
  } else {
    std::cout << "No directory provided" << std::endl;
  }

  return 0;
}

OSX Listing:

$ g++ changedir.c -o changedir
$ ./changedir testing
CWD: /Users/Phil
CWD changed to: /Users/Phil/testing

Centos Listing:

$ g++ changedir.c -o changedir
$ ./changedir
No directory provided
$ ./changedir does_not_exist
CWD: /home/phil
$ ./changedir Music
CWD: /home/phil
CWD changed to: /home/phil/Music
$ ./changedir /
CWD: /home/phil
CWD changed to: /

Win10 Listing

cl.exe changedir.cpp /EHsc /nologo
changedir.cpp

c:\Users\Phil> changedir.exe test
CWD: c:\Users\Phil
CWD changed to: c:\Users\Phil\test

Note: OSX uses clang and Centos gnu gcc behind g++.

Trackless answered 25/10, 2016 at 3:15 Comment(6)
ooh, you put a lot of effort into this. You're leading on my list!Ecbatana
Shorter than with libraries, and clean and neat. Provide a clear explanation of how to set and get the cwd, it's a bit vague right now.Ecbatana
@noɥʇʎPʎzɐɹC let me know if the update isn't clear to you.Trackless
Doesn't work for me on Windows. What is the purpose of the condition argc > 1? Apps without parameters are allowed to have a working directory.Sonorous
@DietrichBaumgarten - are you seeing an error? The argc condition is to protect the indexing into argv in the demonstration program.Trackless
No Phil, everything ok, my mistake. I thought your solution required that the program has arguments, but you only used them for demonstration.Sonorous
M
9

Does chdir() do what you want? It works under both POSIX and Windows.

Mccarron answered 14/8, 2010 at 21:28 Comment(0)
O
5

You want chdir(2). If you are trying to have your program change the working directory of your shell - you can't. There are plenty of answers on SO already addressing that problem.

Oxonian answered 14/8, 2010 at 21:28 Comment(0)
B
5

Did you mean C or C++? They are completely different languages.

In C, the standard that defines the language doesn't cover directories. Many platforms that support directories have a chdir function that takes a char* or const char* argument, but even where it exists the header where it's declared is not standard. There may also be subtleties as to what the argument means (e.g. Windows has per-drive directories).

In C++, googling leads to chdir and _chdir, and suggests that Boost doesn't have an interface to chdir. But I won't comment any further since I don't know C++.

Beaune answered 14/8, 2010 at 21:44 Comment(4)
In boost::filesystem, there wasn't a "chdir" when I used it last time.Bianca
@rubber: indeed, looking at boost.org/doc/libs/1_34_1/boost/filesystem/operations.hpp suggests that there is a getcwd equivalent but no chdir equivalent.Individuate
I could see how one might think C and C++ were completely different languages if they were the only two languages you knew. or if C is the only language you knewObsecrate
@Obsecrate C and C++ have many characteristics in common: they're unsafe, imperative languages. They nonetheless are completely different languages, further apart than, say, C# and Java. It's true that C and C++ have a rather large common subset, but that common subset is almost never good C or good C++. If you think that C is a subset of C++, you're either a bad C programmer, or a bad C++ programmer, or both.Individuate
G
3

Nice cross-platform way to change current directory in C++ was suggested long time ago by @pepper_chico. This solution uses boost::filesystem::current_path().

To get the current working directory use:

namespace fs = boost::filesystem;
fs::path cur_working_dir(fs::current_path());

To set the current working directory use:

namespace fs = boost::filesystem;
fs::current_path(fs::system_complete( fs::path( "new_working_directory_path" ) ));    

Bellow is the self-contained helper functions:

#include "boost/filesystem/operations.hpp"
#include "boost/filesystem/path.hpp"
#include <string>

namespace fs = boost::filesystem;    

fs::path get_cwd_pth()
{
  return fs::current_path();
}   

std::string get_cwd()
{ 
  return get_cwd_pth().c_str();
} 

void set_cwd(const fs::path& new_wd)
{
  fs::current_path(fs::system_complete( new_wd));
}   

void set_cwd(const std::string& new_wd)
{
  set_cwd( fs::path( new_wd));
}

Here is my complete code-example on how to set/get current working directory:

#include "boost/filesystem/operations.hpp"
#include "boost/filesystem/path.hpp"
#include <iostream>

namespace fs = boost::filesystem;

int main( int argc, char* argv[] )
{
  fs::path full_path;
  if ( argc > 1 )
  {
    full_path = fs::system_complete( fs::path( argv[1] ) );
  }  
  else
  {
    std::cout << "Usage:   tcd [path]" << std::endl;
  }

  if ( !fs::exists( full_path ) )
  {
    std::cout << "Not found: " << full_path.c_str() << std::endl;
    return 1;
  }

  if ( !fs::is_directory( full_path ))
  {
    std::cout << "Provided path is not a directory: " << full_path.c_str() << std::endl;
    return 1;
  }

  std::cout << "Old current working directory: " << boost::filesystem::current_path().c_str() << std::endl;

  fs::current_path(full_path);

  std::cout << "New current working directory: " << boost::filesystem::current_path().c_str() << std::endl;
  return 0;
}

If boost installed on your system you can use the following command to compile this sample:

g++ -o tcd app.cpp -lboost_filesystem -lboost_system
Grovergroves answered 24/10, 2016 at 21:41 Comment(7)
give a clean and isolated one-liner for changing and viewing the current working directory.Ecbatana
and a function for those who want it, pleaseEcbatana
@noɥʇʎPʎzɐɹC Added several helper functions to work both with std::string and boost::filesystem::path.Grovergroves
would you mind using lowercase_with_underscores, as this is the typical C++ convention?Ecbatana
@noɥʇʎPʎzɐɹC Not a problem, names updated to comply with boost naming and typical C++ code-style.Grovergroves
I proposed an edit that shortens the names by a lot and changes get_cwd to a function that returns a string, most people will want the string.Ecbatana
@noɥʇʎPʎzɐɹC Thnx, changes accepted. I also shorten a variable names there.Grovergroves
H
2

Can't believe no one has claimed the bounty on this one yet!!!

Here is a cross platform implementation that gets and changes the current working directory using C++. All it takes is a little macro magic, to read the value of argv[0], and to define a few small functions.

Here is the code to change directories to the location of the executable file that is running currently. It can easily be adapted to change the current working directory to any directory you want.

Code :

  #ifdef _WIN32
     #include "direct.h"
     #define PATH_SEP '\\'
     #define GETCWD _getcwd
     #define CHDIR _chdir
  #else
     #include "unistd.h"
     #define PATH_SEP '/'
     #define GETCWD getcwd
     #define CHDIR chdir
  #endif

  #include <cstring>
  #include <string>
  #include <iostream>
  using std::cout;
  using std::endl;
  using std::string;

  string GetExecutableDirectory(const char* argv0) {
     string path = argv0;
     int path_directory_index = path.find_last_of(PATH_SEP);
     return path.substr(0 , path_directory_index + 1);
  }

  bool ChangeDirectory(const char* dir) {return CHDIR(dir) == 0;}

  string GetCurrentWorkingDirectory() {
     const int BUFSIZE = 4096;
     char buf[BUFSIZE];
     memset(buf , 0 , BUFSIZE);
     GETCWD(buf , BUFSIZE - 1);
     return buf;
  }

  int main(int argc , char** argv) {

     cout << endl << "Current working directory was : " << GetCurrentWorkingDirectory() << endl;
     cout << "Changing directory..." << endl;

     string exedir = GetExecutableDirectory(argv[0]);
     ChangeDirectory(exedir.c_str());

     cout << "Current working directory is now : " << GetCurrentWorkingDirectory() << endl;

     return 0;
  }

Output :

c:\Windows>c:\ctwoplus\progcode\test\CWD\cwd.exe

Current working directory was : c:\Windows Changing directory... Current working directory is now : c:\ctwoplus\progcode\test\CWD

c:\Windows>

Hairsplitter answered 22/10, 2016 at 22:21 Comment(6)
Good example. But clean up and shorten your code and it will be yours.Ecbatana
@ noɥʇʎPʎzɐɹC How's that? I shortened it a bit and cleaned it up. Can't shorten it much more.Hairsplitter
Cleaned it up a bit. I'll test it and put an example run for posix and then you'll get the bounty. :)Ecbatana
did a mini code review: gist.github.com/CrazyPython/152805717b1c01649f0efed3415001e0 (it doesn't work on unix)Ecbatana
It works as expected. What do you want it to do? but doesn't escape it gets converted to a stringHairsplitter
Let us continue this discussion in chat.Ecbatana

© 2022 - 2024 — McMap. All rights reserved.