简体   繁体   中英

How to open a file with it's relative path in Linux?

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.

前一段时间有一个问题如何在C中找到可执行文件的位置,您可以使用此路径打开您的配置,资源等。

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.

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 .

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 argv[0] 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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM