c++ – How to open a file with it’s relative path in Linux? – Education Career Blog

I have a program which opens a file by using a relative path (for instance ‘..’).

Now the problem is, when I execute the program from another directory, the relative path is not relative to the program but relative to the working directory. Thus, if I start the program with ‘/path/to/program/myprog’ it fails to find the file.

Is there a way to execute the program independently of the working directory? Id est, as If the working directory were the directory where the program is located? Or am I just thinking in a too complicated way and there is a much easier way to refer to a file, which location is only known by its path relative to the path of the program file?

,

If program is not doing it by itself, it is a bad program. Bad programs should be wrapped with a bit of Bash scripting:

#!/bin/bash

set -e
cd $(readlink -f $(dirname $0))
exec ./myprog $*

The script above determines the directory where it is located, then changes current working directory to that directory and runs a program myprog from there, passing all parameters transparently. Thus, you have to put this script into the same directory where your program is located and run it instead of your program.

Assuming that you have the access to the source code and can fix the program, then use proc fs to determine the program’s location and then use absolute path.

For example, /proc/self/exe will always be a symlink pointing at the binary file of the current process. Use readlink to read its value, then cut executable name and you got the directory.

,

openat opens a file relative to a particular directory file descriptor you pass it, but I don’t think that is really what you want (exactly).

You will need to find the directory where the current executable is, and then create an open call relative to that (using either string operators to build the path, openat, or changing the current directory to that directory).

To find the executable you can readlink /proc/self/exe. readlink reads the path that a symbolic link points to, and /proc/self is a symbolic link to /proc/<PID> where <PID> is the process ID of the current process (handled special in the kernel), and the exe under that is a symbolic link to the executable file for that process. Then you’ll need to fish out the path to that executable and use that.

All of that being said, you usually should avoid writing programs in such a way that they expect to find things relative to their executable file.

,

there has been a question a while ago how to find the location of the executable in C
you could use this path to open your config, resource, etc ..

,

One way is to use argv0 – there is relative path of your program (for example ./programs/test/a.out). If you cut the program name and add the relative path to file, you will get a monster (for example ./programs/test/../../input_data) but it should work.

,

The easiest way would be to either put your program in a pre-known place (/bin, /usr/bin, etc.). If not, you can use the argv0, remove the program name (the last part), and use that as your working directory to prefix all relative paths (if you want relative paths to be relative to where your program is).

Also, you can determine the path of your program using the method above (use argv0), and then call a chdir() with this directory. All relative paths from then on would be relative to where the program is. Note, however, that in this case, you have to determine if argv0 holds an absolute path. If not, you have to get the current working dir (getcwd()) and then append the directory part of argv0. Note, however, that changing the current work dir. is not a good idea, usually, as if a user gives you a file path as an argument, it will be relative to your current work dir, not relative to where the program is stored.

Some examples: Imagine your program lives in /usr/bin. You can call your program as:

/usr/bin/myprog

(that would be argv0. Prune the executable name and you have your dir.) Or, being, say, in /usr:

./bin/myprog

Now, argv0 is a relative path. You have to prepend current working dir (/usr) to the one in the argv0: /usr/./bin/myprog, and then again prune the executable name. The directory would be again /usr/bin.

,

Well, if your program needs to open a file from a location that depends on where the program is installed, you should probably make this a compile-time option. Have your build system set some CPP macro indicating the directory where the data files in question can be found. This is what the –datadir option to configure in a standard “configure, make, make install”-built program often does.

Of course, if you really want to, you can programmatically change the working dir with the chdir POSIX functions. But like I said, if a program needs to know where it is located, this should be provided at compile-time. Then you don’t need to override the user’s choice of working dir.

,

Don’t use relative paths. Use absolute paths. You might have a constant defined in a config.h header file that specifies where your executable is installed. Then, prepend that string constant to any relative path you specify in your code.

,

You can determine the execution path from the argv0 parameter, but be careful when doing so.

What you described is a well known and expected semantic. Users will expect this behaviour.

,

Here is some code you can use to find your install-path from within your program (replace “test0002” with the name of your program):

#include <iostream>
#include <sstream>
#include <string>
#include <fstream>
#include <unistd.h>

///=============================================================================
std::string FindInstallPath()
{
    std::string sret="";
    int pid = (int)getpid();
    bool b=false;
    std::string sf, s;
    std::stringstream ss;
    ss << "/proc/" << pid << "/maps";
    sf = ss.str();
    std::ifstream ifs(sf.c_str());
    size_t pos1, pos2;
    while (!b && ifs.good())
    {
        std::getline(ifs, s);
        if ((pos1 = s.rfind("test0002")) != std::string::npos)
        {
            if ((pos2 = s.find_first_of('/')) != std::string::npos)
            sret = s.substr(pos2, pos1 - pos2);
            b = true;
        }
    }
    if (!b) sret = "";
    ifs.close();
    return sret;
}

Leave a Comment