Мне кажется, что в Linux легко с / proc / self / exe. Но я хотел бы знать, есть ли удобный способ найти каталог текущего приложения в C / C ++ с кросс-платформенными интерфейсами. Я видел, как некоторые проекты возились с argv [0], но это не совсем надежно.
Если бы вам когда-нибудь приходилось поддерживать, скажем, Mac OS X, у которой нет / proc /, что бы вы сделали? Используйте #ifdefs для изоляции кода, специфичного для платформы (например, NSBundle)? Или попробуйте вывести путь к исполняемому файлу из argv [0], $ PATH и прочего, рискуя найти ошибки в крайних случаях?
ps -o comm
. Что привело меня сюда: "/proc/pid/path/a.out"Ответы:
Некоторые специфичные для ОС интерфейсы:
_NSGetExecutablePath()
( мужчина, 3 года )readlink /proc/self/exe
getexecname()
sysctl CTL_KERN KERN_PROC KERN_PROC_PATHNAME -1
readlink /proc/curproc/file
(FreeBSD по умолчанию не имеет procfs)readlink /proc/curproc/exe
readlink /proc/curproc/file
GetModuleFileName()
сhModule
=NULL
Портативный (но менее надежный) метод заключается в использовании
argv[0]
. Хотя вызывающая программа может установить для него что угодно, по соглашению это может быть либо путь к исполняемому файлу, либо имя, которое было найдено с помощью$PATH
.В некоторых оболочках, включая bash и ksh, переменная среды "
_
" задает полный путь к исполняемому файлу до его выполнения. В этом случае вы можете использовать,getenv("_")
чтобы получить его. Однако это ненадежно, потому что не все оболочки делают это, и его можно установить на что-либо или оставить в родительском процессе, который не изменил его до выполнения вашей программы.источник
char exepath[MAXPATHLEN]; sprintf(exepath, "/proc/%d/path/a.out", getpid()); readlink(exepath, exepath, sizeof(exepath));
; это отличается отgetexecname()
- что делает эквивалентpargs -x <PID> | grep AT_SUN_EXECNAME
...Использование
/proc/self/exe
не переносимо и ненадежно. В моей системе Ubuntu 12.04 вы должны быть пользователем root, чтобы читать / следовать символической ссылке. Это сделает пример Boost и, возможно, опубликованныеwhereami()
решения потерпят неудачу.Этот пост очень длинный, но в нем обсуждаются актуальные проблемы и представлен код, который фактически работает вместе с проверкой на соответствие тестовому набору.
Лучший способ найти вашу программу - повторить те же шаги, которые использует система. Это делается с помощью
argv[0]
разрешения файловой системы root, pwd, пути и с учетом символических ссылок и канонизации пути. Это по памяти, но я успешно делал это в прошлом и проверил в различных ситуациях. Он не гарантированно работает, но если этого не произойдет, у вас, вероятно, возникнут гораздо большие проблемы, и он в целом более надежен, чем любой из других обсуждаемых методов. В Unix-совместимой системе существуют ситуации, в которых правильная обработкаargv[0]
не получит вас к вашей программе, но тогда вы выполняете в явно нарушенной среде. Он также достаточно переносим для всех производных систем Unix с 1970 года и даже для некоторых не производных от Unix систем, поскольку в основном он использует стандартные функции libc () и стандартные функции командной строки. Он должен работать на Linux (все версии), Android, Chrome OS, Minix, оригинальной версии Bell Labs Unix, FreeBSD, NetBSD, OpenBSD, BSD xx, SunOS, Solaris, SYSV, HPUX, Concentrix, SCO, Darwin, AIX, OS X, Nextstep и т. Д. И с небольшой модификацией, вероятно, VMS, VM / CMS, DOS / Windows, ReactOS, OS / 2 и т. Д. Если программа была запущена непосредственно из среды графического интерфейса, она должна указыватьargv[0]
абсолютный путь.Поймите, что почти каждая оболочка в каждой Unix-совместимой операционной системе, которая когда-либо выпускалась, в основном находит программы одинаково и настраивает операционную среду почти одинаково (с некоторыми дополнительными дополнениями). Ожидается, что любая другая программа, которая запускает программу, создаст ту же среду (argv, строки окружения и т. Д.) Для этой программы, как если бы она запускалась из оболочки, с некоторыми дополнительными дополнениями. Программа или пользователь могут настроить среду, которая отличается от этого соглашения для других подчиненных программ, которые он запускает, но если это так, то это ошибка, и у программы нет разумных ожиданий, что подчиненная программа или ее подчиненные будут работать правильно.
Возможные значения
argv[0]
включают в себя:/path/to/executable
- абсолютный путь../bin/executable
- относительно pwdbin/executable
- относительно pwd./foo
- относительно pwdexecutable
- имя базы, найти в путиbin//executable
- относительно pwd, неканоническийsrc/../bin/executable
- относительно pwd, неканонический, возвращениеbin/./echoargc
- относительно pwd, неканоническийЗначения, которые вы не должны видеть:
~/bin/executable
Переписать до запуска вашей программы.~user/bin/executable
- переписан до запуска вашей программыalias
- переписан до запуска вашей программы$shellvariable
- переписан до запуска вашей программы*foo*
- подстановочный знак, переписанный до запуска вашей программы, не очень полезный?foo?
- подстановочный знак, переписанный до запуска вашей программы, не очень полезныйКроме того, они могут содержать неканонические имена путей и несколько слоев символических ссылок. В некоторых случаях может быть несколько жестких ссылок на одну и ту же программу. Например,
/bin/ls
,/bin/ps
,/bin/chmod
,/bin/rm
и т.д. , могут быть жесткие ссылки/bin/busybox
.Чтобы найти себя, выполните следующие действия:
Сохраните pwd, PATH и argv [0] при входе в вашу программу (или при инициализации вашей библиотеки), так как они могут измениться позже.
Необязательно: особенно для не-Unix систем, выделите, но не отбрасывайте часть префикса hostname / user / drive, если она есть; часть, которая часто предшествует двоеточию или следует после начального "//".
Если
argv[0]
это абсолютный путь, используйте его в качестве отправной точки. Абсолютный путь, вероятно, начинается с «/», но в некоторых не-Unix системах он может начинаться с «\», либо с буквы диска или префикса имени, за которым следует двоеточие.Иначе, если
argv[0]
это относительный путь (содержит "/" или "\", но не начинается с него, например, "../../bin/foo", затем объедините pwd + "/" + argv [0] (используйте текущая рабочая директория с момента запуска программы, а не текущая).Иначе, если argv [0] - простое базовое имя (без косых черт), затем по очереди объедините его с каждой записью в переменной окружения PATH, попробуйте их и используйте первое, которое завершится успешно.
Дополнительно: Else попробовать самой конкретной платформы
/proc/self/exe
,/proc/curproc/file
(BSD), и(char *)getauxval(AT_EXECFN)
, иdlgetname(...)
если они присутствуют. Вы могли бы даже попробовать эти ранееargv[0]
основанные методы, если они доступны, и у вас нет проблем с разрешениями. В некотором маловероятном случае (когда вы рассматриваете все версии всех систем), что они присутствуют и не выходят из строя, они могут быть более авторитетными.Необязательно: проверьте имя пути, переданное с помощью параметра командной строки.
Необязательно: проверьте путь в среде, явно переданной вашим сценарием-оболочкой, если таковой имеется.
Необязательно: В крайнем случае попробуйте переменную среды "_". Это может указывать на другую программу целиком, например на оболочку пользователя.
Разрешить символические ссылки, может быть несколько слоев. Существует возможность бесконечных циклов, хотя, если они существуют, ваша программа, вероятно, не будет запущена.
Канонизируйте имя файла, разрешив подстроки типа "/foo/../bar/" в "/ bar /". Обратите внимание, что это может потенциально изменить значение, если вы пересекаете точку монтирования сети, поэтому канонизация не всегда хорошая вещь. На сетевом сервере символ «..» в символьной ссылке может использоваться для прохождения пути к другому файлу в контексте сервера, а не на клиенте. В этом случае вам, вероятно, нужен клиентский контекст, поэтому канонизация в порядке. Также конвертируйте шаблоны типа "/./" в "/" и "//" в "/". В оболочке,
readlink --canonicalize
разрешит несколько символических ссылок и канонизирует имя. Чейз может сделать подобное, но не установлен.realpath()
илиcanonicalize_file_name()
, если присутствует, может помочь.Если
realpath()
во время компиляции не существует, вы можете позаимствовать копию из разрешительно лицензированного дистрибутива библиотеки и скомпилировать ее самостоятельно, а не изобретать велосипед. Исправьте потенциальное переполнение буфера (укажите размер выходного буфера, подумайте, что strncpy () vs strcpy ()), если вы будете использовать буфер меньше, чем PATH_MAX. Возможно, будет проще использовать переименованную личную копию, чем тестировать, если она существует. Разрешительная копия лицензии с android / darwin / bsd: https://android.googlesource.com/platform/bionic/+/f077784/libc/upstream-freebsd/lib/libc/stdlib/realpath.cПомните, что несколько попыток могут быть успешными или частично успешными, и не все они могут указывать на один и тот же исполняемый файл, поэтому рассмотрите возможность проверки вашего исполняемого файла; однако у вас может не быть разрешения на чтение - если вы не можете его прочитать, не рассматривайте это как сбой. Или проверьте что-нибудь рядом с вашим исполняемым файлом, например, каталог "../lib/", который вы пытаетесь найти. У вас может быть несколько версий, упакованных и локально скомпилированных версий, локальных и сетевых версий, а также портативных версий локальных и USB-накопителей и т. Д., И существует небольшая вероятность того, что вы можете получить два несовместимых результата из разных методов определения местоположения. И «_» может просто указывать на неправильную программу.
Программа, использующая программу,
execve
может быть намеренно установленаargv[0]
как несовместимая с фактическим путем, используемым для загрузки программы и повреждения PATH, «_», pwd и т. Д., Хотя, как правило, нет особых причин для этого; но это может иметь последствия для безопасности, если у вас есть уязвимый код, который игнорирует тот факт, что среда выполнения может быть изменена различными способами, включая, но не ограничиваясь этим, (chroot, файловая система fuse, жесткие ссылки и т. д.). Возможно для команд оболочки, чтобы установить PATH, но не удается его экспортировать.Вам не обязательно кодировать для не-Unix систем, но было бы неплохо знать о некоторых особенностях, чтобы вы могли написать код таким образом, чтобы потом было не так сложно его портировать , Помните, что некоторые системы (DEC VMS, DOS, URL-адреса и т. Д.) Могут иметь имена дисков или другие префиксы, которые заканчиваются двоеточием, например «C: \», «sys $ drive: [foo] bar» и «file» : /// Foo / бар / Баз». Старые системы DEC VMS используют «[» и «]» для включения части пути в каталог, хотя это может измениться, если ваша программа скомпилирована в среде POSIX. Некоторые системы, такие как VMS, могут иметь версию файла (разделенную точкой с запятой в конце). В некоторых системах используются две последовательные косые черты, такие как «// диск / путь / к / файлу» или «пользователь @ хост: / путь / к / файлу» (команда scp) или «файл: (разделенные пробелами) и "PATH", разделенные двоеточиями, но ваша программа должна получать PATH, поэтому вам не нужно беспокоиться о пути. DOS и некоторые другие системы могут иметь относительные пути, начинающиеся с префикса диска. C: foo.exe относится к foo.exe в текущем каталоге на диске C, поэтому вам нужно найти текущий каталог на C: и использовать его для pwd. (разделенные пробелами) и "PATH", разделенные двоеточиями, но ваша программа должна получать PATH, поэтому вам не нужно беспокоиться о пути. DOS и некоторые другие системы могут иметь относительные пути, начинающиеся с префикса диска. C: foo.exe относится к foo.exe в текущем каталоге на диске C, поэтому вам нужно найти текущий каталог на C: и использовать его для pwd.
Пример символических ссылок и оболочек в моей системе:
Обратите внимание, что пользовательский счет разместил ссылку выше на программу в HP, которая обрабатывает три основных случая
argv[0]
. Это требует некоторых изменений, хотя:strcat()
иstrcpy()
использоватьstrncat()
иstrncpy()
. Даже если переменные объявлены с длиной PATHMAX, входное значение длины PATHMAX-1 плюс длина объединенных строк будет> PATHMAX, а входное значение длины PATHMAX будет не определено.Таким образом, если вы комбинируете код HP и код realpath и исправляете их так, чтобы они были устойчивы к переполнению буфера, то у вас должно быть что-то, что можно правильно интерпретировать
argv[0]
.Ниже показаны действительные значения
argv[0]
для различных способов вызова одной и той же программы в Ubuntu 12.04. И да, программа была случайно названа echoargc вместо echoargv. Это было сделано с использованием сценария для чистого копирования, но выполнение этого вручную в оболочке дает те же результаты (за исключением того, что псевдонимы не работают в сценарии, если вы явно не включили их).Эти примеры иллюстрируют, что методы, описанные в этом посте, должны работать в широком диапазоне обстоятельств и почему необходимы некоторые из шагов.
РЕДАКТИРОВАТЬ: Теперь программа, которая печатает argv [0], была обновлена, чтобы фактически найти себя.
И вот результат, который демонстрирует, что в каждом из предыдущих тестов он действительно находился.
Описанные выше два запуска GUI также корректно находят программу.
Есть одна потенциальная ловушка.
access()
Функция капель разрешения , если программа Setuid перед тестированием. Если есть ситуация, когда программа может быть найдена как пользователь с повышенными правами, но не как обычный пользователь, то может быть ситуация, когда эти тесты не пройдут, хотя маловероятно, что программа действительно может быть выполнена при таких обстоятельствах. Вместо этого можно использовать euidaccess (). Возможно, однако, что он может найти недоступную программу раньше на пути, чем фактический пользователь мог.источник
strncpy()
ни (особенно)strncat()
не используется безопасно в коде.strncpy()
не гарантирует нулевое прекращение; если исходная строка длиннее целевого пространства, строка не заканчивается нулем.strncat()
очень сложно использовать;strncat(target, source, sizeof(target))
неверно (даже еслиtarget
пустая строка для начала), еслиsource
она длиннее целевой. Длина - это количество символов, которое можно безопасно добавить к цели, исключая завершающий ноль, поэтомуsizeof(target)-1
это максимум.Проверьте whereami библиотеку из Грегори Pakosz (который имеет только один файл C); он позволяет вам получить полный путь к текущему исполняемому файлу на различных платформах. В настоящее время он доступен в качестве репо на github здесь .
источник
Альтернатива на Linux с использованием либо
/proc/self/exe
илиargv[0]
используют информацию , передаваемый интерпретатор ELF, доступна по Glibc как таковые:Обратите внимание, что
getauxval
это расширение glibc, и чтобы быть надежным, вы должны проверить, чтобы оно не возвращалосьNULL
(что указывает на то, что интерпретатор ELF не предоставилAT_EXECFN
параметр), но я не думаю, что это на самом деле проблема в Linux.источник
Да, изоляция кода, специфичного для платформы,
#ifdefs
является обычным способом сделать это.Другой подход заключается в том, чтобы иметь
#ifdef
заголовок без очистки, содержащий объявления функций и помещать реализации в исходные файлы для конкретной платформы. Например, посмотрите, как библиотека Poco C ++ делает что-то похожее для своего класса Environment .источник
Надежная работа этой функции на разных платформах требует использования операторов #ifdef.
Приведенный ниже код находит путь к исполняемому файлу в Windows, Linux, MacOS, Solaris или FreeBSD (хотя FreeBSD не тестировалась). Он использует boost > = 1.55.0 для упрощения кода, но его достаточно легко удалить, если хотите. Просто используйте определения, такие как _MSC_VER и __linux, как того требуют операционная система и компилятор.
Вышеприведенная версия возвращает полные пути, включая имя исполняемого файла. Если вместо этого вы хотите указать путь без имени исполняемого файла
#include boost/filesystem.hpp>
и измените оператор return на:источник
В зависимости от версии QNX Neutrino , существуют разные способы найти полный путь и имя исполняемого файла, который использовался для запуска запущенного процесса. Обозначаю идентификатор процесса как
<PID>
. Попробуйте следующее:/proc/self/exefile
существует, то его содержимое является запрошенной информацией./proc/<PID>/exefile
существует, то его содержимое является запрошенной информацией./proc/self/as
существует, то:open()
файл.sizeof(procfs_debuginfo) + _POSIX_PATH_MAX
.devctl(fd, DCMD_PROC_MAPDEBUG_BASE,...
.procfs_debuginfo*
.path
полеprocfs_debuginfo
структуры. Предупреждение : по некоторым причинам, иногда QNX пропускает первую косую черту/
пути к файлу. Подготовьте это,/
когда это необходимо.3.
выполнить процедуру с файлом/proc/<PID>/as
.dladdr(dlsym(RTLD_DEFAULT, "main"), &dlinfo)
гдеdlinfo
этоDl_info
структура,dli_fname
может содержать запрашиваемую информацию.Надеюсь, это поможет.
источник
AFAIK, нет такого способа. И есть также двусмысленность: что бы вы хотели получить в качестве ответа, если один и тот же исполняемый файл имеет несколько жестких ссылок, «указывающих» на него? (Жесткие ссылки на самом деле не «указывают», это один и тот же файл, просто в другом месте в иерархии FS.) Как только execve () успешно выполняет новый двоичный файл, вся информация о его аргументах теряется.
источник
Вы можете использовать argv [0] и проанализировать переменную окружения PATH. Посмотрите на: пример программы, которая может найти себя
источник
execv
и kin выбирают путь к исполняемому файлу отдельно отargv
Более портативный способ получить путь к имени исполняемого образа:
PS может дать вам путь к исполняемому файлу, если у вас есть идентификатор процесса. Также ps - это утилита POSIX, поэтому она должна быть переносимой
поэтому, если идентификатор процесса равен 249297, то эта команда дает вам только имя пути.
Объяснение аргументов
-p - выбирает данный процесс
-o comm - отображает имя команды (-o cmd выбирает всю командную строку)
--no-heading - не отображать строку заголовка, только вывод.
Программа AC может запустить это через popen.
источник
Если вы используете C, вы можете использовать функцию getwd:
Это будет вывести на стандартный вывод текущий каталог исполняемого файла.
источник
Путь абсолютного значения программы находится в PWD envp вашей основной функции, также есть функция в C, называемая getenv, так что это так.
источник