7

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?

Burkhard
  • 14,596
  • 22
  • 87
  • 108
  • 3
    possible duplicate of [Directory of running program on Linux?](http://stackoverflow.com/questions/737996/directory-of-running-program-on-linux) – ereOn Oct 21 '10 at 13:43
  • 1
    This should be achievable however, are you sure you want this ? On Linux, each file type has its dedicated directories : configuration usually go to `/etc/` or ` ~/.my_program`, images and documentations to `/usr/share`, and so on. Also, most of users will expect your program to run in the current directory. – ereOn Oct 21 '10 at 13:45
  • See also http://www.dreamincode.net/forums/topic/98402-directory-of-running-program/ – Joris Meys Oct 21 '10 at 13:46
  • @ereOn: I don't see why it is a duplicate. Maybe the question was not clear enough. My program is always in './Release/bin'. The code is in './tools/something'. Since I sometimes am in the tools directory and sometimes in the root directory, the program fails if I use the relative path in the program. – Burkhard Oct 21 '10 at 13:54
  • @Burkhard: Once you have the program absolute path, changing the current directory is trivial (`chdir`) so what you ask is basically equals to: "How do I get the absolute path of my executable ?". – ereOn Oct 21 '10 at 14:12
  • @ereOn: I hadn't thought of that. You are right: from that point it is trivial. – Burkhard Oct 21 '10 at 18:10

9 Answers9

4

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.

  • 1
    Isn't it kinda unconventional to override the user's choice of working directory like this (or programmatically using `chdir` as I mentioned)? – gspr Oct 21 '10 at 13:45
  • @gspr: Bash script does not override user's working directory. It overrides working directory for the executable that it runs. Because it needs it. User's directory is not getting changed. –  Oct 21 '10 at 13:47
  • 1
    I meant the user's choice of working directory *for this program*. Isn't doing so sort of bad practice? I mean, as wilhelmtell, Let_Me_Be and I all point out, there are better ways to achieve the desired goal. – gspr Oct 21 '10 at 13:54
  • 2
    Yes, it is kind of a bad practice. But if program is dumb enough to use hard-coded relative path to access files and you cannot really fix the code, this is one of the ways to go. It also depends what program is doing etc. Other solution might be to run it from some other directory and just provide all files in the relative path it expects. –  Oct 21 '10 at 14:00
  • 1
    You should probably pass in `"$@"` rather than `$*` – wilhelmtell Oct 21 '10 at 14:37
2

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.

nategoose
  • 12,054
  • 27
  • 42
1

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 ..

Community
  • 1
  • 1
Nikolaus Gradwohl
  • 19,708
  • 3
  • 45
  • 61
1

One way is to use argv[0] - 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.

Adam Trhon
  • 2,915
  • 1
  • 20
  • 51
  • This is not guaranteed, is it ? – ereOn Oct 21 '10 at 13:47
  • This is assuming he can modify the program. The question is about how to execute the program in its directory, not how to modify the program and fix this bug with relative path. –  Oct 21 '10 at 13:48
  • How did I forget argv[0]? It can be inaccurate since it is set by the `execve` call, but it is usually correct and should be good enough to use for this. – nategoose Oct 21 '10 at 13:48
  • @Vlad Lazarenko: Since the OP tagged the question `c++` I guess we can assume that he has full control over the program. – ereOn Oct 21 '10 at 13:49
  • 1
    @ereOn: No. In Linux, proc file system should be used. I can do `exec` and pass "Clowns attack the world" as `argv[0]` and the whole thing will fail. –  Oct 21 '10 at 13:49
  • 1
    @Vlad Lazarenko: Thanks, wasn't sure. Glad to see you don't like clowns either btw. – ereOn Oct 21 '10 at 13:50
  • @Vlad Lazarenko: At the extreme you can build Linux kernel without /proc – Adam Trhon Oct 21 '10 at 14:01
  • @Dadam it is much harder to do than passing clown into argv[0]. Plus, many programs in Linux will just stop working. –  Oct 21 '10 at 14:07
1

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 argv[0], 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 argv[0]), 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 argv[0] holds an absolute path. If not, you have to get the current working dir (getcwd()) and then append the directory part of argv[0]. 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 argv[0]. Prune the executable name and you have your dir.) Or, being, say, in /usr:

./bin/myprog

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

Diego Sevilla
  • 28,636
  • 4
  • 59
  • 87
0

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.

gspr
  • 11,144
  • 3
  • 41
  • 74
0

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.

wilhelmtell
  • 57,473
  • 20
  • 96
  • 131
  • This would then rely on the program being installed in a specific location and would break functionality if the directory is moved. – Chris Watts May 11 '16 at 19:27
0

You can determine the execution path from the argv[0] parameter, but be careful when doing so.

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

Šimon Tóth
  • 35,456
  • 20
  • 106
  • 151
0

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;
}
slashmais
  • 7,069
  • 9
  • 54
  • 80