在C++中更改当前工作目录

62

如何以跨平台的方式更改C++中的当前工作目录?

我找到了direct.h 头文件,适用于Windows,还有unistd.h,适用于UNIX/POSIX。


@noɥʇʎPʎzɐɹC 所以标准委员会已经通过 filesystem 在 C++17 中确立了一种更改工作目录的标准方式。pepper_chico 的回答 已经指出了这一点。filesystem 目前在 g++5.3 和 Visual Studio 2015 中作为可选包含文件提供。如果您正在使用这个环境,我可以使用 #ifdef 为您编写一个跨平台访问 filesystem 的答案。 - Jonathan Mee
@JonathanMee 如果足够好的话,我可能会设置多个赏金。 - noɥʇʎԀʎzɐɹƆ
9个回答

59

现在,使用C++17可以使用std::filesystem::current_path函数:

#include <filesystem>
int main() {
    auto path = std::filesystem::current_path(); //getting path
    std::filesystem::current_path(path); //setting path
}

这仅更改当前进程的路径。操作 shell 的当前路径不会更改。 - Marche Remi
6
通常情况下,当您想要更改当前工作目录时,就意味着要这样做。在大多数操作系统下,无法更改任何其他进程的工作目录。 - HiddenWindshield

53

chdir 函数适用于 POSIX(manpage)和 Windows(在那里被称为 _chdir,但别名 chdir 也存在)。

两种实现都在成功时返回零,在出错时返回 -1。如您在 manpage 中所见,POSIX 版本中可能存在更多区分的 errno 值,但对于大多数用例来说,这并不重要。


1
@sparkFinder,当处理非标准函数(如chdir())时,通常需要在不同的平台上包含不同的头文件。如果针对Windows进行编译,GCC会定义_WIN32,因此您可以使用#include与之配合选择一个头文件。 - RBerteig
3
你可以使用 #ifdef _MSC_VER 检查 Visual Studio,并包含 direct.h 头文件。如果未定义,则使用 unistd.h。 这应该足够了,因为在 Windows 上的另一个主要编程环境 MinGW已有 unistd.h 头文件。 - AndiDog
2
在 Windows 平台上,chdir 已经被弃用。 - noɥʇʎԀʎzɐɹƆ
@noɥʇʎPʎzɐɹC 在此页面上没有关于它被弃用的任何说明。你的信息来源是什么? - dbush
1
@dbush _chdir != chdir _chdir 不是跨平台的,而 chdir 已经被弃用。 - Jonathan Mee
显示剩余3条评论

21

20

这是一个跨平台的示例代码,用于使用POSIX的chdir和MS的_chdir来更改工作目录,建议参考此答案。同样对于确定当前工作目录,使用类似的getcwd_getcwd

这些平台差异被隐藏在宏cdcwd背后。

根据文档,chdir的签名是int chdir(const char * path),其中path可以是绝对或相对路径。成功时chdir将返回0。 getcwd稍微复杂一些,因为它需要(在一个变体中)用于存储获取的路径的缓冲区,如char * getcwd(char * buf,size_t size)所示。失败时返回NULL,成功时返回指向相同传递的缓冲区的指针。代码示例直接利用了该返回的字符指针。

该示例基于@MarcD的示例,但更正了内存泄漏。此外,我力求简洁,没有依赖项,并且只进行基本的失败/错误检查,以确保它在多个(常见)平台上运行。

我在OSX 10.11.6、Centos7和Win10上进行了测试。对于OSX和Centos,我使用g++ changedir.cpp -o changedir进行构建,并作为./changedir <path>运行。

在Win10上,我使用cl.exe changedir.cpp /EHsc /nologo进行构建。

MVP解决方案

$ 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清单:

$ g++ changedir.c -o changedir
$ ./changedir testing
当前工作目录: /Users/Phil
工作目录更改为:/Users/Phil/testing

Centos清单:

$ g++ changedir.c -o changedir
$ ./changedir
未提供目录
$ ./changedir does_not_exist
当前工作目录: /home/phil
$ ./changedir Music
当前工作目录: /home/phil
工作目录更改为: /home/phil/Music
$ ./changedir /
当前工作目录: /home/phil
工作目录更改为: /

Win10清单:

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

c:\Users\Phil> changedir.exe test
当前工作目录: c:\Users\Phil
工作目录更改为: c:\Users\Phil\test

注意:OSX使用clang,Centos使用gcc(在g++背后)。


1
哦,你在这方面付出了很多努力。你在我的名单上排名第一! - noɥʇʎԀʎzɐɹƆ
比使用库更短,而且干净整洁。请提供如何设置和获取cwd的清晰说明,目前有点模糊不清。 - noɥʇʎԀʎzɐɹƆ
@noɥʇʎPʎzɐɹC 如果更新内容不清楚,请告诉我。 - Phil
在Windows上对我不起作用。条件argc > 1的目的是什么?允许没有参数的应用程序具有工作目录。 - Dietrich Baumgarten
@DietrichBaumgarten - 你看到错误了吗?argc条件是为了保护演示程序中对argv的索引。 - Phil
不用了,一切都好。是我自己的错误,我以为你的解决方案需要程序有参数,但你只是用它们做演示。 - Dietrich Baumgarten

9

chdir()函数是否符合您的需求?它可以在POSIX和Windows下使用。


5
您想使用chdir(2)。如果您试图让程序更改您的shell的工作目录 - 您无法这样做。 SO上已经有很多回答解决了这个问题。

5
你是指C还是C++?它们是完全不同的语言。
在C语言中,定义该语言的标准不包括目录。许多支持目录的平台都有一个接受char*或const char*参数的chdir函数,但即使存在此函数,声明该函数的头文件也不是标准的。还可能会存在关于参数意义的微妙差别(例如,Windows有针对每个驱动器的目录)。
在C++中,通过搜索,可以得到chdir和_chdir函数,并且建议使用Boost来调用chdir。但由于我不了解C++,因此不再发表评论。

在我上次使用boost::filesystem时,它没有提供"chdir"函数。 - rubber boots
@rubber:确实,查看http://www.boost.org/doc/libs/1_34_1/boost/filesystem/operations.hpp表明有一个类似于`getcwd`的函数,但没有相应的`chdir`函数。 - Gilles 'SO- stop being evil'
如果你只知道C语言或者C++语言,那么你可能会认为它们是完全不同的编程语言。 - Michael
1
@Michael C和C++有许多共同点:它们都是不安全的命令式语言。然而,它们是完全不同的语言,比如C#和Java之间的差距更大。的确,C和C++有一个相当大的共同子集,但这个共同子集几乎从来不是好的C或好的C++。如果你认为C是C++的子集,那么你要么是一个糟糕的C程序员,要么是一个糟糕的C++程序员,或者两者兼而有之。 - Gilles 'SO- stop being evil'

3
很久以前,@pepper_chico 提出了一种优雅的 C++ 跨平台方式来更改当前目录。该解决方案使用 boost::filesystem::current_path()。 要获取当前工作目录,请使用:
namespace fs = boost::filesystem;
fs::path cur_working_dir(fs::current_path());

使用以下命令设置当前工作目录:

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

以下是自包含的辅助函数:
#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));
}

这是一个完整的编程示例,演示如何设置/获取当前工作目录:

#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;
}

如果您的系统上安装了boost,您可以使用以下命令编译此示例:
g++ -o tcd app.cpp -lboost_filesystem -lboost_system

提供一个简洁且独立的一行代码,用于更改和查看当前工作目录。 - noɥʇʎԀʎzɐɹƆ
提供一个函数,以便需要的人使用。 - noɥʇʎԀʎzɐɹƆ
1
@noɥʇʎPʎzɐɹC 添加了几个辅助函数,可以同时处理 std::stringboost::filesystem::path - Nikita
1
@noɥʇʎPʎzɐɹC 没问题,名称已更新以符合 boost 命名和典型的 C++ 代码风格。 - Nikita
1
@noɥʇʎPʎzɐɹC 谢谢,更改已接受。我还缩短了一个变量名。 - Nikita
显示剩余2条评论

2

难以相信没有人领取这个奖金!

以下是一个使用C ++获取和更改当前工作目录的跨平台实现。只需要一点点宏魔法,就可以读取argv [0]的值,并定义几个小函数。

下面是将目录更改为当前运行的可执行文件所在位置的代码。它可以轻松地适应于将当前工作目录更改为任何您想要的目录。

代码:

  #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;
  }

输出:

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

当前工作目录为:c:\Windows 正在更改目录... 当前工作目录已更改为:c:\ctwoplus\progcode\test\CWD

c:\Windows>


好的例子。但是请整理并缩短你的代码,它就会属于你了。 - noɥʇʎԀʎzɐɹƆ
@noɥʇʎPʎzɐɹC 这样怎么样?我把它缩短了一点并且整理了一下。不能再缩短了。 - MarcD
稍微整理了一下。我会测试它并提供一个 posix 的示例运行,然后你就可以得到赏金。 :) - noɥʇʎԀʎzɐɹƆ
1
做了一个小的代码审查:https://gist.github.com/CrazyPython/152805717b1c01649f0efed3415001e0(它在Unix上不起作用) - noɥʇʎԀʎzɐɹƆ
它按预期工作。你想让它做什么?但是它不会转义,而是被转换为字符串。 - MarcD
让我们在聊天中继续这个讨论 - noɥʇʎԀʎzɐɹƆ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接