Basename and Dirname

April 10th, 2009 § 1 comment

Often the unix commands basename and dirname are useful in shell scripts, but how do you use these in c or c++?.

The C way: libgen.h

If you’re working in c, the posix header libgen.h provides dirname and basename functions, with some limitations: They only work with char *, and dirname will modify the char * you pass into it so you need to make a copy of your original path if you wish to keep it. Example code:

// libgen_example.c
// Demonstrate libgen.h usage and pitfalls
// Compile:  gcc -g -o libgen_example libgen_example.c
#include
<libgen.h> //basename and dirname
#include <string.h> //strlen and strcpy
#include <stdio.h> //printf
#include <stdlib.h> //malloc
 
int main(int argc, char* argv[]) {
    // Demonstrate how dirname changes the path you input to it
    char path[] = "default/path/to/nowhere.txt";
    printf("path='%s'\n", path);
    char * dir = dirname(path);
    printf("path='%s'  dir='%s'\n", path, dir);
    char * base = basename(path);
    printf("path='%s'  dir='%s'  base='%s'\n\n",
           path, dir, base);
    // Since dirname changed path, base is now "to"
    // instead of "nowhere.txt" as we might have expected.
    // Output:
    // path='default/path/to/nowhere.txt'
    // path='default/path/to'  dir='default/path/to'
    // path='default/path/to'  dir='default/path/to'  base='to'
 
    //========
    // Demonstrate c string copying
    const char * another_path = "another/path";
    char * modifiable_copy = (char *)malloc(strlen(another_path) + 1);
    strcpy(modifiable_copy, another_path);
    char * another_dir = dirname(modifiable_copy);
    printf("another_path='%s'\n", another_path);
    printf("modifiable_copy='%s'\n", modifiable_copy);
    printf("another_dir='%s'\n\n", another_dir);
    // note that after this free, another_dir is now also
    // invalid since it pointed to the modifiable_copy's memory
    free(modifiable_copy);
    // Output:
    // another_path='another/path'
    // modifiable_copy='another'
    // another_dir='another'
 
    //========
    // const strings declared as char * can cause segfaults
    char * char_pointer_path = "char/pointer/path";
    printf("char_pointer_path='%s'\n", char_pointer_path);
 
    char * char_pointer_dir = dirname(char_pointer_path);
    printf("char_pointer_path='%s' char_pointer_dir='%s'\n",
           char_pointer_path, char_pointer_dir);
    // Output:
    // char_pointer_path='char/pointer/path'
    // Segmentation fault
}

The C++ way: boost::filesystem

If you’re looking for a better c++ solution, see the c++ boost filesystem library. boost::filesystem::path.parent_path() can be used like dirname and boost::filesystem::path.filename() acts like basename.

The boost::filesystem library also has many other useful functions for dealing with files and paths (e.g. fs::complete(path) for obtaining an absolute path, fs::exists(path) will check if the path exists, fs::create_directories(path), fs::remove(path), fs::copy_file(from,to) name a few). Note that some of these are functions are boost::filesystem::path methods and some are in boost::filesystem. According to the faq, operations done by the operating system are provided in boost::filesystem functions, but functions just performed on the lexical path (just path string manipulations) are provided as boost::filesystem::path methods.

// filesystem_example.cpp
// Demonstrate using boost::filesystem
// Compile:  g++ -I /usr/local/include/boost-1_38/ -L /usr/local/lib -lboost_filesystem-gcc41-mt-1_38 -o filesystem_example filesystem_example.cpp
#include <boost/filesystem.hpp>
#include <iostream>
namespace fs = boost::filesystem;
 
int main(int argc, char* argv[])
{
    fs::path pathname("default/path/to/nowhere.txt");
    std::string dirname  = pathname.parent_path().string();
    std::string basename = pathname.filename();
 
    std::cout << "pathname = " << pathname << std::endl;
    std::cout << "dirname = " << dirname << std::endl;
    std::cout << "basename = " << basename << std::endl;
    // Output:
    // pathname = default/path/to/nowhere.txt
    // dirname = default/path/to
    // basename = nowhere.txt
 
    std::cout << std::endl;
    std::cout << "extension = " << pathname.extension() << std::endl;
    std::cout << "complete = " << fs::complete(pathname) << std::endl;
    std::cout << "exists = " << ((fs::exists(pathname)) ? "true" : "false") << std::endl;
    // Output:
    // extension = .txt
    // complete = /home/moose/default/path/to/nowhere.txt
    // exists = false
}

Tagged , , , , , , , , ,

  • http://blog.yegle.net/ yegle

    std::string basename = pathname.filename().string();

    Should be in this way?