Me parece que Linux lo tiene fácil con / proc / self / exe. Pero me gustaría saber si hay una manera conveniente de encontrar el directorio de la aplicación actual en C / C ++ con interfaces multiplataforma. He visto algunos proyectos jugando con argv [0], pero no parece del todo confiable.
Si alguna vez tuvieras que soportar, por ejemplo, Mac OS X, que no tiene / proc /, ¿qué hubieras hecho? ¿Usa #ifdefs para aislar el código específico de la plataforma (NSBundle, por ejemplo)? ¿O intente deducir la ruta del ejecutable de argv [0], $ PATH y otras cosas, arriesgándose a encontrar errores en casos extremos?
ps -o comm
. Lo que me trajo aquí es: "/proc/pid/path/a.out"Respuestas:
Algunas interfaces específicas del sistema operativo:
_NSGetExecutablePath()
( hombre 3 dyld )readlink /proc/self/exe
getexecname()
sysctl CTL_KERN KERN_PROC KERN_PROC_PATHNAME -1
readlink /proc/curproc/file
(FreeBSD no tiene procfs por defecto)readlink /proc/curproc/exe
readlink /proc/curproc/file
GetModuleFileName()
conhModule
=NULL
El método portátil (pero menos confiable) es usar
argv[0]
. Si bien el programa que realiza la llamada puede establecerlo en cualquier cosa, por convención se configura como un nombre de ruta del ejecutable o un nombre que se encontró utilizando$PATH
.Algunos shells, incluidos bash y ksh, establecen la variable de entorno "
_
" en la ruta completa del ejecutable antes de que se ejecute. En ese caso puedes usarlogetenv("_")
para conseguirlo. Sin embargo, esto no es confiable porque no todos los shells hacen esto, y podría establecerse en cualquier cosa o dejarse de un proceso principal que no lo cambió antes de ejecutar su programa.fuente
char exepath[MAXPATHLEN]; sprintf(exepath, "/proc/%d/path/a.out", getpid()); readlink(exepath, exepath, sizeof(exepath));
; eso es diferente degetexecname()
- lo que hace el equivalente depargs -x <PID> | grep AT_SUN_EXECNAME
...El uso de
/proc/self/exe
no es portátil y no es confiable. En mi sistema Ubuntu 12.04, debe ser root para leer / seguir el enlace simbólico. Esto hará que el ejemplo de Boost y probablemente laswhereami()
soluciones publicadas fallen.Esta publicación es muy larga, pero analiza los problemas reales y presenta el código que realmente funciona junto con la validación contra un conjunto de pruebas.
La mejor manera de encontrar su programa es volver sobre los mismos pasos que utiliza el sistema. Esto se hace mediante el uso
argv[0]
resuelto contra la raíz del sistema de archivos, pwd, entorno de ruta y considerando enlaces simbólicos y canonicalización de nombre de ruta. Esto es de memoria, pero lo he hecho en el pasado con éxito y lo probé en una variedad de situaciones diferentes. No se garantiza que funcione, pero si no es así, probablemente tenga problemas mucho mayores y, en general, es más confiable que cualquiera de los otros métodos discutidos. Hay situaciones en un sistema compatible con Unix en el que el manejo adecuado deargv[0]
no lo llevará a su programa, pero luego se está ejecutando en un entorno roto que puede certificarse. También es bastante portátil para todos los sistemas derivados de Unix desde alrededor de 1970 e incluso algunos sistemas no derivados de Unix, ya que básicamente se basa en la funcionalidad estándar libc () y la funcionalidad de línea de comandos estándar. Debería funcionar en Linux (todas las versiones), Android, Chrome OS, Minix, Bell Labs Unix original, FreeBSD, NetBSD, OpenBSD, BSD xx, SunOS, Solaris, SYSV, HPUX, Concentrix, SCO, Darwin, AIX, OS X, Nextstep, etc. Y con una pequeña modificación, probablemente VMS, VM / CMS, DOS / Windows, ReactOS, OS / 2, etc. Si un programa se lanzó directamente desde un entorno GUI, debería haberse establecidoargv[0]
en una ruta absoluta.Comprenda que casi todos los shell de todos los sistemas operativos compatibles con Unix que se han lanzado básicamente encuentran los programas de la misma manera y configuran el entorno operativo de la misma manera (con algunos extras opcionales). Y se espera que cualquier otro programa que inicie un programa cree el mismo entorno (argv, cadenas de entorno, etc.) para ese programa como si se ejecutara desde un shell, con algunos extras opcionales. Un programa o usuario puede configurar un entorno que se desvía de esta convención para otros programas subordinados que inicia, pero si lo hace, es un error y el programa no tiene una expectativa razonable de que el programa subordinado o sus subordinados funcionarán correctamente.
Los posibles valores de
argv[0]
incluyen:/path/to/executable
- camino absoluto../bin/executable
- relativo a pwdbin/executable
- relativo a pwd./foo
- relativo a pwdexecutable
- nombre base, encontrar en el caminobin//executable
- relativo a pwd, no canónicosrc/../bin/executable
- relativo a pwd, no canónico, retrocesobin/./echoargc
- relativo a pwd, no canónicoValores que no deberías ver:
~/bin/executable
- reescrito antes de que se ejecute su programa.~user/bin/executable
- reescrito antes de que se ejecute tu programaalias
- reescrito antes de que se ejecute tu programa$shellvariable
- reescrito antes de que se ejecute tu programa*foo*
- comodín, reescrito antes de que se ejecute el programa, no muy útil?foo?
- comodín, reescrito antes de que se ejecute el programa, no muy útilAdemás, estos pueden contener nombres de ruta no canónicos y múltiples capas de enlaces simbólicos. En algunos casos, puede haber múltiples enlaces duros al mismo programa. Por ejemplo,
/bin/ls
,/bin/ps
,/bin/chmod
,/bin/rm
, etc., pueden ser enlaces duros/bin/busybox
.Para encontrarse, siga los pasos a continuación:
Guarde pwd, PATH y argv [0] en la entrada a su programa (o inicialización de su biblioteca) ya que pueden cambiar más adelante.
Opcional: particularmente para sistemas que no son Unix, separe pero no descarte la parte del prefijo host / usuario / unidad de nombre de ruta, si está presente; la parte que a menudo precede a dos puntos o sigue a un "//" inicial.
Si
argv[0]
es una ruta absoluta, úsela como punto de partida. Una ruta absoluta probablemente comienza con "/" pero en algunos sistemas que no son Unix puede comenzar con "\" o una letra de unidad o prefijo de nombre seguido de dos puntos.De lo contrario, si
argv[0]
es una ruta relativa (contiene "/" o "\" pero no comienza con ella, como "../../bin/foo", luego combine pwd + "/" + argv [0] (use presente directorio de trabajo desde el inicio del programa, no actual).De lo contrario, si argv [0] es un nombre base simple (sin barras), entonces combínelo con cada entrada en la variable de entorno PATH y pruebe esas y use la primera que tenga éxito.
Opcional: Else tratar la específica plataforma muy
/proc/self/exe
,/proc/curproc/file
(BSD), y(char *)getauxval(AT_EXECFN)
, ydlgetname(...)
si está presente. Incluso puede probar estosargv[0]
métodos anteriores, si están disponibles y no encuentra problemas de permisos. En el caso poco probable (si considera todas las versiones de todos los sistemas) que están presentes y no fallan, podrían ser más autoritativos.Opcional: verifique el nombre de la ruta que se pasa utilizando un parámetro de línea de comando.
Opcional: compruebe si hay un nombre de ruta en el entorno que su secuencia de comandos pasa explícitamente, si corresponde.
Opcional: como último recurso, pruebe la variable de entorno "_". Puede apuntar a un programa completamente diferente, como el shell de los usuarios.
Resolver enlaces simbólicos, puede haber varias capas. Existe la posibilidad de bucles infinitos, aunque si existen, su programa probablemente no será invocado.
Canonicalice el nombre de archivo resolviendo subcadenas como "/foo/../bar/" a "/ bar /". Tenga en cuenta que esto puede cambiar el significado si cruza un punto de montaje de red, por lo que la canonización no siempre es algo bueno. En un servidor de red, ".." en el enlace simbólico se puede usar para recorrer una ruta a otro archivo en el contexto del servidor en lugar de hacerlo en el cliente. En este caso, probablemente desee el contexto del cliente, por lo que la canonicalización está bien. También convierta patrones como "/./" a "/" y "//" a "/". En shell,
readlink --canonicalize
resolverá múltiples enlaces simbólicos y canonizará el nombre. Chase puede hacer algo similar pero no está instalado.realpath()
ocanonicalize_file_name()
, si está presente, puede ayudar.Si
realpath()
no existe en el momento de la compilación, puede pedir prestada una copia de una distribución de biblioteca autorizada y autorizada, y compilarla usted mismo en lugar de reinventar la rueda. Arregle el desbordamiento potencial del búfer (pase el tamaño del búfer de salida, piense en strncpy () vs strcpy ()) si va a usar un búfer menor que PATH_MAX. Puede ser más fácil usar una copia privada renombrada en lugar de probar si existe. Copia de licencia permisiva de android / darwin / bsd: https://android.googlesource.com/platform/bionic/+/f077784/libc/upstream-freebsd/lib/libc/stdlib/realpath.cTenga en cuenta que varios intentos pueden ser exitosos o parcialmente exitosos y es posible que no todos apunten al mismo ejecutable, así que considere verificar su ejecutable; sin embargo, es posible que no tenga permiso de lectura; si no puede leerlo, no lo trate como un error. O verifique algo cerca de su ejecutable, como el directorio "../lib/" que está tratando de encontrar. Es posible que tenga varias versiones, versiones compiladas y compiladas localmente, versiones locales y de red, y versiones portátiles de unidades locales y USB, etc. y existe una pequeña posibilidad de que obtenga dos resultados incompatibles de diferentes métodos de localización. Y "_" puede simplemente señalar el programa incorrecto.
Un programa que usa
execve
puede establecer deliberadamenteargv[0]
para que sea incompatible con la ruta real utilizada para cargar el programa y corromper PATH, "_", pwd, etc. aunque generalmente no hay muchas razones para hacerlo; pero esto podría tener implicaciones de seguridad si tiene un código vulnerable que ignora el hecho de que su entorno de ejecución se puede cambiar de varias maneras, incluyendo, entre otras, esta (chroot, sistema de archivos de fusibles, enlaces duros, etc.) Es posible para que los comandos de shell establezcan PATH pero no lo exporten.No es necesario que codifique para sistemas que no son Unix, pero sería una buena idea conocer algunas de las peculiaridades para poder escribir el código de tal manera que no sea tan difícil que alguien lo transfiera más tarde. . Tenga en cuenta que algunos sistemas (DEC VMS, DOS, URL, etc.) pueden tener nombres de unidades u otros prefijos que terminan con dos puntos como "C: \", "sys $ drive: [foo] bar" y "file : /// foo / bar / baz ". Los sistemas DEC VMS antiguos usan "[" y "]" para encerrar la parte del directorio de la ruta, aunque esto puede haber cambiado si su programa se compila en un entorno POSIX. Algunos sistemas, como VMS, pueden tener una versión de archivo (separados por un punto y coma al final). Algunos sistemas utilizan dos barras diagonales consecutivas como en "// unidad / ruta / a / archivo" o "usuario @ host: / ruta / a / archivo" (comando scp) o "archivo: (delimitado con espacios) y "PATH" delimitado con dos puntos, pero su programa debe recibir PATH para que no tenga que preocuparse por la ruta. DOS y algunos otros sistemas pueden tener rutas relativas que comienzan con un prefijo de unidad. C: foo.exe se refiere a foo.exe en el directorio actual en la unidad C, por lo que debe buscar el directorio actual en C: y usarlo para pwd. (delimitado con espacios) y "PATH" delimitado con dos puntos, pero su programa debe recibir PATH para que no tenga que preocuparse por la ruta. DOS y algunos otros sistemas pueden tener rutas relativas que comienzan con un prefijo de unidad. C: foo.exe se refiere a foo.exe en el directorio actual en la unidad C, por lo que debe buscar el directorio actual en C: y usarlo para pwd.
Un ejemplo de enlaces simbólicos y envoltorios en mi sistema:
Tenga en cuenta que la factura del usuario publicó un enlace arriba a un programa en HP que maneja los tres casos básicos de
argv[0]
. Sin embargo, necesita algunos cambios:strcat()
ystrcpy()
usarstrncat()
ystrncpy()
. Aunque las variables se declaran de longitud PATHMAX, un valor de entrada de longitud PATHMAX-1 más la longitud de las cadenas concatenadas es> PATHMAX y un valor de entrada de longitud PATHMAX no estaría terminado.Entonces, si combina tanto el código de HP como el código de ruta real y arregla ambos para que sean resistentes a los desbordamientos del búfer, entonces debe tener algo que pueda interpretar correctamente
argv[0]
.A continuación se ilustran los valores reales de
argv[0]
varias formas de invocar el mismo programa en Ubuntu 12.04. Y sí, el programa se llamó accidentalmente echoargc en lugar de echoargv. Esto se hizo usando un script para una copia limpia, pero hacerlo manualmente en el shell obtiene los mismos resultados (excepto que los alias no funcionan en el script a menos que los habilites explícitamente).Estos ejemplos ilustran que las técnicas descritas en esta publicación deberían funcionar en una amplia gama de circunstancias y por qué algunos de los pasos son necesarios.
EDITAR: Ahora, el programa que imprime argv [0] se ha actualizado para encontrarse realmente.
Y aquí está el resultado que demuestra que en cada una de las pruebas anteriores se encontró realmente.
Los dos lanzamientos de GUI descritos anteriormente también encuentran correctamente el programa.
Hay un peligro potencial. La
access()
función elimina los permisos si el programa se establece antes de la prueba. Si hay una situación en la que el programa se puede encontrar como un usuario elevado pero no como un usuario normal, entonces puede haber una situación en la que estas pruebas fallarían, aunque es poco probable que el programa se ejecute en esas circunstancias. Uno podría usar euidaccess () en su lugar. Sin embargo, es posible que encuentre un programa inaccesible antes en la ruta que el usuario real.fuente
strncpy()
ni (especialmente)strncat()
se usa de manera segura en el código.strncpy()
no garantiza la terminación nula; Si la cadena de origen es más larga que el espacio de destino, la cadena no tiene terminación nula.strncat()
es muy dificil de usar;strncat(target, source, sizeof(target))
está mal (incluso sitarget
es una cadena vacía para empezar) sisource
es más larga que el objetivo. La longitud es el número de caracteres que se pueden agregar de forma segura al objetivo, excluyendo el nulo final, por lo quesizeof(target)-1
es el máximo.Echa un vistazo a la biblioteca whereami de Gregory Pakosz (que tiene un solo archivo C); le permite obtener la ruta completa al ejecutable actual en una variedad de plataformas. Actualmente, está disponible como un repositorio en github aquí .
fuente
Una alternativa en Linux para usar cualquiera
/proc/self/exe
oargv[0]
es usar la información pasada por el intérprete ELF, que glibc pone a disposición como tal:Tenga en cuenta que
getauxval
es una extensión glibc, y para ser robusto, debe verificar que no regreseNULL
(lo que indica que el intérprete ELF no ha proporcionado elAT_EXECFN
parámetro), pero no creo que esto sea realmente un problema en Linux.fuente
Sí, aislar el código específico de la plataforma
#ifdefs
es la forma convencional de hacerlo.Otro enfoque sería tener un
#ifdef
encabezado sin limpieza que contenga declaraciones de funciones y coloque las implementaciones en archivos fuente específicos de la plataforma. Por ejemplo, vea cómo la biblioteca Poco C ++ hace algo similar para su clase de entorno .fuente
Hacer que esto funcione de manera confiable en todas las plataformas requiere el uso de declaraciones #ifdef.
El siguiente código encuentra la ruta del ejecutable en Windows, Linux, MacOS, Solaris o FreeBSD (aunque FreeBSD no ha sido probado). Utiliza boost > = 1.55.0 para simplificar el código, pero es lo suficientemente fácil de eliminar si lo desea. Simplemente use definiciones como _MSC_VER y __linux según lo requiera el sistema operativo y el compilador.
La versión anterior devuelve rutas completas, incluido el nombre del ejecutable. Si, en cambio, desea la ruta sin el nombre del ejecutable
#include boost/filesystem.hpp>
y cambia la declaración de retorno a:fuente
Dependiendo de la versión de QNX Neutrino , hay diferentes formas de encontrar la ruta completa y el nombre del archivo ejecutable que se utilizó para iniciar el proceso de ejecución. Denote el identificador de proceso como
<PID>
. Intenta lo siguiente:/proc/self/exefile
existe, su contenido es la información solicitada./proc/<PID>/exefile
existe, su contenido es la información solicitada./proc/self/as
existe, entonces:open()
el archivo.sizeof(procfs_debuginfo) + _POSIX_PATH_MAX
,.devctl(fd, DCMD_PROC_MAPDEBUG_BASE,...
.procfs_debuginfo*
.path
campo de laprocfs_debuginfo
estructura. Advertencia : por alguna razón, a veces, QNX omite la primera barra diagonal/
de la ruta del archivo. Anteponga eso/
cuando sea necesario.3.
con el archivo/proc/<PID>/as
.dladdr(dlsym(RTLD_DEFAULT, "main"), &dlinfo)
dóndedlinfo
está unaDl_info
estructura cuyo contenidodli_fname
podría contener la información solicitada.Espero que esto ayude.
fuente
AFAIK, no es así. Y también hay una ambigüedad: ¿qué le gustaría obtener como respuesta si el mismo ejecutable tiene múltiples enlaces duros "apuntando"? (Los enlaces duros en realidad no "apuntan", son el mismo archivo, solo que en otro lugar de la jerarquía FS.) Una vez que execve () ejecuta con éxito un nuevo binario, se pierde toda la información sobre sus argumentos.
fuente
Puede usar argv [0] y analizar la variable de entorno PATH. Mire: una muestra de un programa que puede encontrarse
fuente
execv
parientes toman el camino hacia el ejecutable por separado deargv
Forma más portátil de obtener el nombre de ruta de la imagen ejecutable:
ps puede darle la ruta del ejecutable, dado que tiene la identificación del proceso. Ps también es una utilidad POSIX, por lo que debería ser portátil
entonces, si la identificación del proceso es 249297, entonces este comando solo le da el nombre de la ruta.
Explicación de argumentos
-p: selecciona el proceso dado
-o comm: muestra el nombre del comando (-o cmd selecciona la línea de comando completa)
--no-header - no muestra una línea de encabezado, solo la salida.
El programa de CA puede ejecutar esto a través de popen.
fuente
Si usa C, puede usar la función getwd:
Esto imprimirá en la salida estándar, el directorio actual del ejecutable.
fuente
La ruta de valor absoluto de un programa está en el PWD del entorno de su función principal, también hay una función en C llamada getenv, así que ahí está.
fuente