Как мне найти расположение исполняемого файла в C? [Дубликат]

130

Есть ли способ в C/С++ найти местоположение (полный путь) текущей исполняемой программы?

(Проблема с argv[0] заключается в том, что он не дает полного пути.)

  • 11
    какая операционная система?
  • 0
    Я не думаю, что есть портативный способ сделать это. Есть ли у argv [0] полный путь, если вы вызываете программу с полным статическим путем? Если это так, вы можете заставить пользователя выполнить двоичный файл как таковой, как это делает sshd.
Показать ещё 4 комментария
Теги:
path

9 ответов

183
Лучший ответ

Подводя итог:

  • В Unixes с /proc действительно прямой и реалистичный способ:

    • readlink("/proc/self/exe", buf, bufsize) (Linux)

    • readlink("/proc/curproc/file", buf, bufsize) (FreeBSD)

    • readlink("/proc/self/path/a.out", buf, bufsize) (Solaris)

  • В Unixes без /proc (т.е. если выше не удается):

    • Если argv [0] начинается с "/" (абсолютный путь), это путь.

    • В противном случае, если argv [0] содержит "/" (относительный путь), добавьте его в cwd (предполагая, что он еще не изменен).

    • В противном случае поиск каталогов в $PATH для исполняемого argv[0].

    Впоследствии может быть разумным проверить, не является ли исполняемый файл фактически символической ссылкой. Если он разрешает это относительно каталога symlink.

    Этот шаг не требуется в/proc-методе (по крайней мере для Linux). Там процилинговая ссылка proc указывает на исполняемый файл.

    Обратите внимание, что процесс вызова должен корректно установить argv[0]. Это правильно в большинстве случаев, однако есть случаи, когда вызывающему процессу нельзя доверять (например, исполняемый файл setuid).

  • В Windows: используйте GetModuleFileName(NULL, buf, bufsize)

  • 0
    +1 хороший острый ответ
  • 21
    Все, что зависит от того, является ли argv [0] именем программы, ненадежно. Это будет работать большую часть времени, но не каждый раз. Эта проблема трудна для юниксов без / proc
Показать ещё 13 комментариев
18

Используйте функцию GetModuleFileName(), если вы используете Windows.

  • 2
    Спасибо, но я использую Linux и Unix.
15

Обратите внимание, что следующие комментарии являются unix-only.

Педантичный ответ на этот вопрос заключается в том, что во всех случаях нет правильного общего способа ответить на этот вопрос. Как вы обнаружили, argv [0] может быть настроен на что-либо вообще родительским процессом и поэтому не должен иметь никакого отношения к фактическому имени программы или ее местоположению в файловой системе.

Тем не менее, следующая эвристика часто работает:

  • Если argv [0] является абсолютным путем, предположим, что это полный путь к исполняемому файлу.
  • Если argv [0] - относительный путь, т.е. он содержит /, определите текущий рабочий каталог с помощью getcwd(), а затем добавьте к нему argv [0].
  • Если argv [0] является простым словом, найдите $PATH, ища argv [0], и добавьте argv [0] в любой каталог, в котором вы его нашли.

Обратите внимание, что все это можно обойти процессом, который вызвал рассматриваемую программу. Наконец, вы можете использовать специфичные для Linux методы, например, упомянутые emg-2. В других операционных системах, вероятно, есть эквивалентные методы.

Даже если предположить, что приведенные выше шаги дают вам правильное имя пути, вы все равно можете не иметь имя пути, которое вам действительно нужно (поскольку я подозреваю, что то, что вы на самом деле хотите сделать, это найти файл конфигурации где-нибудь). Наличие жестких ссылок означает, что вы можете иметь следующую ситуацию:

-- assume /app/bin/foo is the actual program
$ mkdir /some/where/else
$ ln /app/bin/foo /some/where/else/foo     # create a hard link to foo
$ /some/where/else/foo

Теперь подход выше (в том числе, я подозреваю, /proc/ $pid/exe) даст /some/where/else/foo как реальный путь к программе. И, фактически, это реальный путь к программе, а не тот, который вы хотели. Обратите внимание, что эта проблема не возникает с символическими ссылками, которые на практике гораздо более распространены, чем жесткие ссылки.

Несмотря на то, что этот подход в принципе ненадежный, он работает достаточно хорошо на практике для большинства целей.

9

На самом деле это не ответ, а просто примечание, которое нужно иметь в виду.

Как мы могли видеть, проблема поиска местоположения исполняемого исполняемого файла довольно сложна и специфична для платформы в Linux и Unix. Перед этим нужно подумать дважды.

Если вам требуется ваше место для поиска файлов конфигурации или ресурсов, возможно, вам следует следовать методу Unix по размещению файлов в системе: поместите конфиги в /etc или /usr/local/etc или в текущий домашний каталог пользователя и /usr/share - хорошее место для размещения ваших файлов ресурсов.

6

Во многих системах POSIX вы можете проверить simlink, расположенную под /proc/PID/exe. Несколько примеров:

# file /proc/*/exe
/proc/1001/exe: symbolic link to /usr/bin/distccd
/proc/1023/exe: symbolic link to /usr/sbin/sendmail.sendmail
/proc/1043/exe: symbolic link to /usr/sbin/crond
  • 12
    / proc не POSIX и не очень стандартизирован. У многих современных Unices это есть, у некоторых нет.
  • 0
    Всегда хорошо учиться новым вещам. Благодарю. Есть ли более «программный» способ сделать это?
Показать ещё 4 комментария
4

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

/proc/self/exe будет по-прежнему доступен для чтения, но на самом деле это не будет действующая символическая ссылка. Это будет... странно.

  • 0
    Unix загружает все исполняемые программы в память перед запуском? В таком случае, как он может иметь достаточно памяти для запуска очень больших программ, таких как некоторые самораспаковывающиеся архивы?
  • 1
    Вообще нет. Исполняемый файл заблокирован (попытка открыть для записи приводит к «текстовому файлу занято») и «отображению памяти», что означает, что он выглядит так, как будто все находится в памяти, но он будет лениво загружаться при первом обращении к странице памяти. Если это страница только для чтения (как обычно бывает в коде), тогда ядро может «забыть» данные, если ему требуется память, и повторно загрузить их, когда к ним снова получат доступ. Вроде как обмен, но поскольку код не изменен, он никогда не будет записан обратно на диск.
Показать ещё 2 комментария
3

В Mac OS X используйте _NSGetExecutablePath.

См. man 3 dyld и этот ответ по аналогичному вопросу.

3

Для Linux вы можете найти способ /proc/self/exe делать вещи в комплекте с хорошей библиотекой binreloc, вы можете найти библиотеку по адресу:

  • 0
    +1 за библиотеку BinReloc.
  • 2
    ссылка мертва :(
0

Я бы

1) Используйте функцию basename(): http://linux.die.net/man/3/basename
2) chdir() в этот каталог
3) Используйте getpwd(), чтобы получить текущий каталог

Таким образом, вы получите каталог в аккуратной, полной форме, а не./или../bin/.

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню