Как открыть файл с его относительным путем в Linux?

У меня есть программа, которая открывает файл, используя относительный путь (например, "..").

Теперь проблема в том, что когда я запускаю программу из другого каталога, относительный путь не относительно программы, а относительно рабочего каталога. Таким образом, если я запускаю программу с "/ path / to / program / myprog", она не может найти файл.

Есть ли способ выполнить программу независимо от рабочего каталога? То есть, как если бы рабочий каталог был каталогом, в котором находится программа? Или я просто слишком сложным образом думаю, и существует гораздо более простой способ обратиться к файлу, местоположение которого известно только по его пути относительно пути к файлу программы?

9 ответов

Решение

Если программа не делает это сама по себе, это плохая программа. Плохие программы должны быть обернуты небольшим количеством скриптов Bash:

#!/bin/bash

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

Сценарий выше определяет каталог, в котором он находится, затем меняет текущий рабочий каталог на этот каталог и запускает программу myprog оттуда, передав все параметры прозрачно. Таким образом, вы должны поместить этот скрипт в тот же каталог, где находится ваша программа, и запустить его вместо вашей программы.

Предполагая, что у вас есть доступ к исходному коду и вы можете исправить программу, используйте proc fs для определения местоположения программы и затем используйте абсолютный путь.

Например, /proc/self/exe всегда будет символической ссылкой, указывающей на двоичный файл текущего процесса. использование readlink чтобы прочитать его значение, затем вырезать имя исполняемого файла, и вы получите каталог.

openat открывает файл относительно определенного дескриптора файла каталога, который вы передаете, но я не думаю, что это действительно то, что вы хотите (именно).

Вам нужно будет найти каталог, в котором находится текущий исполняемый файл, а затем создать открытый вызов относительно него (используя строковые операторы для построения пути, openatили изменение текущего каталога на этот каталог).

Найти исполняемый файл вы можете readlink/proc/self/exe, readlink читает путь, на который указывает символическая ссылка, и /proc/self является символической ссылкой на /proc/<PID> где <PID> является идентификатором текущего процесса (обрабатывается специальным образом в ядре), а exe под ним находится символическая ссылка на исполняемый файл для этого процесса. Затем вам нужно найти путь к этому исполняемому файлу и использовать его.

При всем этом, как правило, вы должны избегать написания программ таким образом, чтобы они ожидали найти что-то относительно своего исполняемого файла.

Одним из способов является использование argv[0] - существует относительный путь вашей программы (например, ./programs/test/a.out). Если вы урежете имя программы и добавите относительный путь к файлу, вы получите монстра (например, ./programs/test/../../input_data) но это должно работать.

Самый простой способ - поместить вашу программу в заранее известное место (/bin, /usr/bin и т. Д.). Если нет, вы можете использовать argv[0], удалить имя программы (последнюю часть) и использовать его в качестве рабочего каталога для префикса всех относительных путей (если вы хотите, чтобы относительные пути были относительно того, где находится ваша программа).

Кроме того, вы можете определить путь к вашей программе, используя метод выше (используйте argv[0]), а затем позвоните chdir() с этим каталогом. Все относительные пути с этого момента будут относительно того, где находится программа. Обратите внимание, однако, что в этом случае вы должны определить, argv[0] держит абсолютный путь. Если нет, вы должны получить текущий рабочий каталог (getcwd()), а затем добавить часть каталога argv[0], Обратите внимание, однако, что изменение текущей работы реж. Обычно это не очень хорошая идея, так как если пользователь указывает вам путь к файлу в качестве аргумента, он будет относительно вашего текущего рабочего каталога, а не относительно того, где хранится программа.

Несколько примеров: представьте, что ваша программа живет в /usr/bin, Вы можете назвать свою программу как:

/usr/bin/myprog

(это было бы argv[0], Удалите имя исполняемого файла, и у вас есть каталог.) Или, скажем, в /usr:

./bin/myprog

Сейчас, argv[0] это относительный путь. Вы должны добавить текущий рабочий каталог (/usr) к одному в argv[0]: /usr/./bin/myprog, а затем снова удалите имя исполняемого файла. Каталог будет снова /usr/bin,

Некоторое время назад был вопрос, как найти расположение исполняемого файла в C, вы можете использовать этот путь, чтобы открыть конфигурацию, ресурс и т. д.

Вот код, который вы можете использовать, чтобы найти путь установки из вашей программы (замените "test0002" на имя вашей программы):

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

Вы можете определить путь выполнения из argv[0] параметр, но будьте осторожны при этом.

То, что вы описали, является хорошо известной и ожидаемой семантикой. Пользователи будут ожидать такого поведения.

Что ж, если вашей программе нужно открыть файл из местоположения, которое зависит от того, где установлена ​​программа, вы, вероятно, должны сделать это параметром времени компиляции. Попросите систему сборки установить какой-либо макрос CPP, указывающий каталог, в котором можно найти файлы с данными. Это то, что часто делает опция --datadir для настройки в стандартной программе "configure, make, make install".

Конечно, если вы действительно хотите, вы можете программно изменить рабочий каталог с помощью chdir POSIX функции. Но, как я уже сказал, если программе нужно знать, где она находится, это должно быть предоставлено во время компиляции. Тогда вам не нужно отменять выбор пользователем рабочего каталога.

Не используйте относительные пути. Используйте абсолютные пути. В заголовочном файле config.h может быть определена константа, которая указывает, где установлен ваш исполняемый файл. Затем добавьте эту строковую константу к любому относительному пути, который вы указали в своем коде.

Другие вопросы по тегам