how compile compilar c++ c linux macos executable

c++ - compilar - how to compile c



Encontrar la ruta del ejecutable actual sin/proc/self/exe (12)

Me parece que Linux lo tiene fácil con / proc / self / exe. Pero me gustaría saber si existe una forma conveniente de encontrar el directorio de la aplicación actual en C / C ++ con interfaces multiplataforma. He visto algunos proyectos dando vueltas con argv [0], pero no parece del todo confiable.

Si alguna vez tuvo que admitir, por ejemplo, Mac OS X, que no tiene / proc /, ¿qué hubiera hecho? Use #ifdefs para aislar el código específico de la plataforma (NSBundle, por ejemplo) ¿O intenta deducir la ruta del ejecutable desde argv [0], $ PATH y otras cosas, arriesgándose a encontrar errores en casos extremos?


Si alguna vez tuvo que admitir, por ejemplo, Mac OS X, que no tiene / proc /, ¿qué hubiera hecho? Use #ifdefs para aislar el código específico de la plataforma (NSBundle, por ejemplo)

Sí, el código específico de la plataforma de aislamiento con #ifdefs es la forma convencional de hacerlo.

Otro enfoque sería tener un encabezado limpio #ifdef -less que contenga declaraciones de funciones y colocar 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 Environment .


AFAIK, no de esa manera. Y también hay una ambigüedad: ¿qué le gustaría obtener como respuesta si el mismo ejecutable tiene múltiples enlaces duros "apuntando" a él? (Los enlaces duros en realidad no "apuntan", son el mismo archivo, solo en otro lugar en la jerarquía FS). Una vez que execve () ejecuta con éxito un nuevo binario, se pierde toda la información sobre sus argumentos.


Algunas interfaces específicas del sistema operativo:

El método portátil (pero menos confiable) es usar argv[0] . Aunque el programa de llamada puede establecerlo en cualquier cosa, por convención se establece en una ruta de acceso del ejecutable o en un nombre que se encontró usando $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 usar getenv("_") para obtenerlo. Sin embargo, esto no es confiable porque no todas las shells hacen esto, y podría configurarse para cualquier cosa o ser sobrante de un proceso principal que no lo cambió antes de ejecutar su programa.


Dependiendo de la versión de QNX Neutrino , existen diferentes formas de encontrar la ruta completa y el nombre del archivo ejecutable que se utilizó para iniciar el proceso en ejecución. Denuncio el identificador del proceso como <PID> . Pruebe lo siguiente:

  1. Si el archivo /proc/self/exefile existe, entonces su contenido es la información solicitada.
  2. Si el archivo /proc/<PID>/exefile existe, entonces su contenido es la información solicitada.
  3. Si el archivo /proc/self/as existe, entonces:
    1. open() el archivo.
    2. Asigne un búfer de, al menos, sizeof(procfs_debuginfo) + _POSIX_PATH_MAX .
    3. Dele ese buffer como entrada a devctl(fd, DCMD_PROC_MAPDEBUG_BASE,...
    4. Emite el búfer a procfs_debuginfo* .
    5. La información solicitada se encuentra en el campo de path de path de la estructura procfs_debuginfo . Advertencia : por alguna razón, a veces, QNX omite la primera barra inclinada / de la ruta del archivo. Anteponga eso / cuando sea necesario.
    6. Limpiar (cerrar el archivo, liberar el buffer, etc.).
  4. Pruebe el procedimiento en 3. con el archivo /proc/<PID>/as .
  5. Pruebe dladdr(dlsym(RTLD_DEFAULT, "main"), &dlinfo) donde dlinfo es una estructura dli_fname cuyo dli_fname podría contener la información solicitada.

Espero que esto ayude.


Echa un vistazo a la biblioteca de 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 here .


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 whereami() el ejemplo de Boost y probablemente las soluciones de whereami() publicadas.

Esta publicación es muy larga, pero analiza los problemas reales y presenta un código que realmente funciona junto con la validación de un conjunto de pruebas.

La mejor manera de encontrar su programa es seguir los mismos pasos que usa el sistema. Esto se hace usando argv[0] resuelto contra raíz del sistema de archivos, pwd, entorno de ruta y considerando enlaces simbólicos, y canonicalización de nombres de ruta. Esto es de memoria, pero lo hice en el pasado con éxito y lo probé en una variedad de situaciones diferentes. No está garantizado que funcione, pero si no es probable que tenga problemas mucho más grandes y es más confiable en general que cualquiera de los otros métodos discutidos. Hay situaciones en un sistema compatible con Unix en el que el manejo adecuado de argv[0] no lo llevará a su programa, pero luego se está ejecutando en un entorno que se puede romper. 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 de libc () y en la funcionalidad de línea de comando estándar. Debería funcionar en Linux (todas las versiones), Android, Chrome OS, Minix, Unix originales Labs de laboratorio, 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 haber configurado argv[0] en un valor absoluto camino.

Comprenda que casi cada caparazón en cada sistema operativo compatible con Unix que se haya lanzado, básicamente encuentra los programas de la misma manera y configura el entorno operativo casi de la misma manera (con algunos extras opcionales). Y se espera que cualquier otro programa que lance 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, se trata de un error y el programa no tiene ninguna expectativa razonable de que el programa subordinado o sus subordinados funcionen correctamente.

Los valores posibles de argv[0] incluyen:

  • /path/to/executable - ruta absoluta
  • ../bin/executable - relativo a pwd
  • bin/executable - relativo a pwd
  • ./foo - relativo a pwd
  • executable - nombre base, encontrar en el camino
  • bin//executable - relativo a pwd, no canónico
  • src/../bin/executable - relativo a pwd, non-canonical, backtracking
  • bin/./echoargc - relativo a pwd, no canónico

Valores que no deberías ver:

  • ~/bin/executable - reescrito antes de que se ejecute su programa.
  • ~user/bin/executable : reescrito antes de que se ejecute el programa
  • alias : reescrito antes de que se ejecute el programa
  • $shellvariable - reescrito antes de que se ejecute su programa
  • *foo* : comodín, reescrito antes de que se ejecute el programa, no es muy útil
  • ?foo? - comodín, reescrito antes de que se ejecute el programa, no es muy útil

Además, estos pueden contener nombres de ruta no canónicos y múltiples capas de enlaces simbólicos. En algunos casos, puede haber enlaces duros múltiples al mismo programa. Por ejemplo, /bin/ls , /bin/ps , /bin/chmod , /bin/rm , etc. pueden ser enlaces duros a /bin/busybox .

Para encontrarte a ti mismo, sigue los pasos a continuación:

  • Guarde pwd, PATH y argv [0] al ingresar a su programa (o inicialización de su biblioteca) ya que pueden cambiar más tarde.

  • Opcional: particularmente para sistemas que no son de Unix, separe por separado pero no descarte la parte del nombre de la ruta principal / usuario / prefijo de la unidad, si está presente; la parte que a menudo precede a dos puntos o sigue un "//" inicial.

  • Si argv[0] es una ruta absoluta, utilícela como punto de partida. Una ruta absoluta probablemente comienza con "/", pero en algunos sistemas que no son de Unix podría comenzar con "/" o una letra de unidad o un 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", entonces combina pwd + "/" + argv [ 0] (use el directorio de trabajo actual desde que se inició el programa, no actual).

  • De lo contrario, si argv [0] es un nombre de pila simple (sin barras), a continuación, combínelo con cada entrada en la variable de entorno PATH y pruebe esas y utilice la primera que tenga éxito.

  • Opcional: De lo contrario, pruebe la plataforma específica /proc/self/exe , /proc/curproc/file (BSD), y (char *)getauxval(AT_EXECFN) y dlgetname(...) si está presente. Incluso puede probar estos métodos antes de argv[0] , si están disponibles y no encuentra problemas de permisos. En el caso poco probable (cuando se consideran todas las versiones de todos los sistemas) de que están presentes y no fallan, podrían ser más autoritativas.

  • Opcional: compruebe si se ha pasado un nombre de ruta usando un parámetro de línea de comando.

  • Opcional: busque un nombre de ruta en el entorno explícitamente pasado por su script de contenedor, si lo hay.

  • Opcional: como último recurso pruebe la variable de entorno "_". Podría apuntar a un programa diferente por completo, 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 enlace simbólico se puede utilizar para recorrer una ruta a otro archivo en el contexto del servidor en lugar de en el cliente. En este caso, probablemente desee el contexto del cliente, por lo que la canonicalización es correcta. También convierta patrones como "/./" a "/" y "//" a "/". En shell, readlink --canonicalize resolverá múltiples enlaces simbólicos y canonicalizará el nombre. Chase puede hacer algo similar pero no está instalado. realpath() o canonicalize_file_name() , si está presente, puede ayudar.

Si realpath() no existe en el momento de la compilación, puede tomar prestada una copia de una distribución autorizada de bibliotecas y compilarla en su lugar en vez de reinventar la rueda. Arregle el posible desbordamiento del búfer (pase en el tamaño del búfer de salida, piense en strncpy () frente a strcpy ()) si va a utilizar un búfer menor que PATH_MAX. Puede ser más fácil simplemente 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.c

Tenga en cuenta que los intentos múltiples pueden ser exitosos o parcialmente exitosos y es posible que no todos apunten al mismo ejecutable, por lo tanto, 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. Puede tener múltiples versiones, versiones empaquetadas y compiladas localmente, versiones locales y de red, y versiones portátiles locales y de unidades USB, etc. y hay una pequeña posibilidad de que pueda obtener 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 deliberadamente que argv[0] sea ​​incompatible con la ruta real utilizada para cargar el programa y corrompa 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 puede cambiarse de diversas maneras, incluidas, entre otras, las de este (sistema de archivos chroot, fusible, enlaces duros, etc.). Es posible para los comandos del shell para configurar PATH pero no exportarlo.

No necesariamente necesita codificar los sistemas que no son de Unix, pero sería una buena idea conocer algunas de las peculiaridades para que pueda escribir el código de tal manera que no sea tan difícil para alguien portarlo 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 $ unidad: [foo] barra" y "archivo" : /// foo / bar / baz ". Los viejos sistemas DEC VMS usan "[" y "]" para encerrar la parte del directorio de la ruta aunque esto puede haber cambiado si su programa está compilado 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 usan dos barras consecutivas como en "// unidad / ruta / a / archivo" o "usuario @ host: / ruta / a / archivo" (comando scp) o "archivo: // nombre de host / ruta / a / archivo" (URL) En algunos casos (DOS, windoze), PATH puede tener diferentes caracteres de separación - ";" vs ":" y "/" frente a "/" para un separador de ruta. En csh / tsh hay "ruta" (delimitada con espacios) y "RUTA" delimitada con dos puntos pero su programa debe recibir RUTA 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 necesita buscar el directorio actual en C: y usarlo para pwd.

Un ejemplo de enlaces simbólicos y envoltorios en mi sistema:

/usr/bin/google-chrome is symlink to /etc/alternatives/google-chrome which is symlink to /usr/bin/google-chrome-stable which is symlink to /opt/google/chrome/google-chrome which is a bash script which runs /opt/google/chome/chrome

Tenga en cuenta que la bill usuario posted un enlace arriba a un programa en HP que maneja los tres casos básicos de argv[0] . Sin embargo, necesita algunos cambios:

  • Será necesario volver a escribir todos los strcat() y strcpy() para usar strncat() y strncpy() . 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.
  • Necesita ser reescrito como una función de biblioteca, en lugar de simplemente imprimir resultados.
    • No puede canonicalizar los nombres (use el código de ruta real al que he vinculado anteriormente)
    • No puede resolver enlaces simbólicos (use el código de ruta real)

Por lo tanto, si combina tanto el código HP como el código de ruta real y corrige ambos para que sea resistente a los desbordamientos del búfer, entonces debe tener algo que pueda interpretar correctamente argv[0] .

Lo siguiente ilustra los valores reales de argv[0] para varias formas de invocar el mismo programa en Ubuntu 12.04. Y sí, el programa se llamó accidentalmente ecoargc en lugar de echoargv. Esto se hizo usando una secuencia de comandos para la copia limpia, pero al hacerlo manualmente en el shell obtiene los mismos resultados (excepto que los alias no funcionan en la secuencia de comandos a menos que los habilite explícitamente).

cat ~/src/echoargc.c #include <stdlib.h> #include <stdio.h> #include <unistd.h> main(int argc, char **argv) { printf(" argv[0]=/"%s/"/n", argv[0]); sleep(1); /* in case run from desktop */ } tcc -o ~/bin/echoargc ~/src/echoargc.c cd ~ /home/whitis/bin/echoargc argv[0]="/home/whitis/bin/echoargc" echoargc argv[0]="echoargc" bin/echoargc argv[0]="bin/echoargc" bin//echoargc argv[0]="bin//echoargc" bin/./echoargc argv[0]="bin/./echoargc" src/../bin/echoargc argv[0]="src/../bin/echoargc" cd ~/bin *echo* argv[0]="echoargc" e?hoargc argv[0]="echoargc" ./echoargc argv[0]="./echoargc" cd ~/src ../bin/echoargc argv[0]="../bin/echoargc" cd ~/junk ~/bin/echoargc argv[0]="/home/whitis/bin/echoargc" ~whitis/bin/echoargc argv[0]="/home/whitis/bin/echoargc" alias echoit=~/bin/echoargc echoit argv[0]="/home/whitis/bin/echoargc" echoarg=~/bin/echoargc $echoarg argv[0]="/home/whitis/bin/echoargc" ln -s ~/bin/echoargc junk1 ./junk1 argv[0]="./junk1" ln -s /home/whitis/bin/echoargc junk2 ./junk2 argv[0]="./junk2" ln -s junk1 junk3 ./junk3 argv[0]="./junk3" gnome-desktop-item-edit --create-new ~/Desktop # interactive, create desktop link, then click on it argv[0]="/home/whitis/bin/echoargc" # interactive, right click on gnome application menu, pick edit menus # add menu item for echoargc, then run it from gnome menu argv[0]="/home/whitis/bin/echoargc" cat ./testargcscript 2>&1 | sed -e ''s/^/ /g'' #!/bin/bash # echoargc is in ~/bin/echoargc # bin is in path shopt -s expand_aliases set -v cat ~/src/echoargc.c tcc -o ~/bin/echoargc ~/src/echoargc.c cd ~ /home/whitis/bin/echoargc echoargc bin/echoargc bin//echoargc bin/./echoargc src/../bin/echoargc cd ~/bin *echo* e?hoargc ./echoargc cd ~/src ../bin/echoargc cd ~/junk ~/bin/echoargc ~whitis/bin/echoargc alias echoit=~/bin/echoargc echoit echoarg=~/bin/echoargc $echoarg ln -s ~/bin/echoargc junk1 ./junk1 ln -s /home/whitis/bin/echoargc junk2 ./junk2 ln -s junk1 junk3 ./junk3

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.

// Copyright 2015 by Mark Whitis. License=MIT style #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <limits.h> #include <assert.h> #include <string.h> #include <errno.h> // "look deep into yourself, Clarice" -- Hanibal Lector char findyourself_save_pwd[PATH_MAX]; char findyourself_save_argv0[PATH_MAX]; char findyourself_save_path[PATH_MAX]; char findyourself_path_separator=''/''; char findyourself_path_separator_as_string[2]="/"; char findyourself_path_list_separator[8]=":"; // could be ":; " char findyourself_debug=0; int findyourself_initialized=0; void findyourself_init(char *argv0) { getcwd(findyourself_save_pwd, sizeof(findyourself_save_pwd)); strncpy(findyourself_save_argv0, argv0, sizeof(findyourself_save_argv0)); findyourself_save_argv0[sizeof(findyourself_save_argv0)-1]=0; strncpy(findyourself_save_path, getenv("PATH"), sizeof(findyourself_save_path)); findyourself_save_path[sizeof(findyourself_save_path)-1]=0; findyourself_initialized=1; } int find_yourself(char *result, size_t size_of_result) { char newpath[PATH_MAX+256]; char newpath2[PATH_MAX+256]; assert(findyourself_initialized); result[0]=0; if(findyourself_save_argv0[0]==findyourself_path_separator) { if(findyourself_debug) printf(" absolute path/n"); realpath(findyourself_save_argv0, newpath); if(findyourself_debug) printf(" newpath=/"%s/"/n", newpath); if(!access(newpath, F_OK)) { strncpy(result, newpath, size_of_result); result[size_of_result-1]=0; return(0); } else { perror("access failed 1"); } } else if( strchr(findyourself_save_argv0, findyourself_path_separator )) { if(findyourself_debug) printf(" relative path to pwd/n"); strncpy(newpath2, findyourself_save_pwd, sizeof(newpath2)); newpath2[sizeof(newpath2)-1]=0; strncat(newpath2, findyourself_path_separator_as_string, sizeof(newpath2)); newpath2[sizeof(newpath2)-1]=0; strncat(newpath2, findyourself_save_argv0, sizeof(newpath2)); newpath2[sizeof(newpath2)-1]=0; realpath(newpath2, newpath); if(findyourself_debug) printf(" newpath=/"%s/"/n", newpath); if(!access(newpath, F_OK)) { strncpy(result, newpath, size_of_result); result[size_of_result-1]=0; return(0); } else { perror("access failed 2"); } } else { if(findyourself_debug) printf(" searching $PATH/n"); char *saveptr; char *pathitem; for(pathitem=strtok_r(findyourself_save_path, findyourself_path_list_separator, &saveptr); pathitem; pathitem=strtok_r(NULL, findyourself_path_list_separator, &saveptr) ) { if(findyourself_debug>=2) printf("pathitem=/"%s/"/n", pathitem); strncpy(newpath2, pathitem, sizeof(newpath2)); newpath2[sizeof(newpath2)-1]=0; strncat(newpath2, findyourself_path_separator_as_string, sizeof(newpath2)); newpath2[sizeof(newpath2)-1]=0; strncat(newpath2, findyourself_save_argv0, sizeof(newpath2)); newpath2[sizeof(newpath2)-1]=0; realpath(newpath2, newpath); if(findyourself_debug) printf(" newpath=/"%s/"/n", newpath); if(!access(newpath, F_OK)) { strncpy(result, newpath, size_of_result); result[size_of_result-1]=0; return(0); } } // end for perror("access failed 3"); } // end else // if we get here, we have tried all three methods on argv[0] and still haven''t succeeded. Include fallback methods here. return(1); } main(int argc, char **argv) { findyourself_init(argv[0]); char newpath[PATH_MAX]; printf(" argv[0]=/"%s/"/n", argv[0]); realpath(argv[0], newpath); if(strcmp(argv[0],newpath)) { printf(" realpath=/"%s/"/n", newpath); } find_yourself(newpath, sizeof(newpath)); if(1 || strcmp(argv[0],newpath)) { printf(" findyourself=/"%s/"/n", newpath); } sleep(1); /* in case run from desktop */ }

Y aquí está el resultado que demuestra que en cada una de las pruebas anteriores realmente se encontró.

tcc -o ~/bin/echoargc ~/src/echoargc.c cd ~ /home/whitis/bin/echoargc argv[0]="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" echoargc argv[0]="echoargc" realpath="/home/whitis/echoargc" findyourself="/home/whitis/bin/echoargc" bin/echoargc argv[0]="bin/echoargc" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" bin//echoargc argv[0]="bin//echoargc" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" bin/./echoargc argv[0]="bin/./echoargc" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" src/../bin/echoargc argv[0]="src/../bin/echoargc" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" cd ~/bin *echo* argv[0]="echoargc" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" e?hoargc argv[0]="echoargc" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" ./echoargc argv[0]="./echoargc" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" cd ~/src ../bin/echoargc argv[0]="../bin/echoargc" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" cd ~/junk ~/bin/echoargc argv[0]="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" ~whitis/bin/echoargc argv[0]="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" alias echoit=~/bin/echoargc echoit argv[0]="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" echoarg=~/bin/echoargc $echoarg argv[0]="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" rm junk1 junk2 junk3 ln -s ~/bin/echoargc junk1 ./junk1 argv[0]="./junk1" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" ln -s /home/whitis/bin/echoargc junk2 ./junk2 argv[0]="./junk2" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc" ln -s junk1 junk3 ./junk3 argv[0]="./junk3" realpath="/home/whitis/bin/echoargc" findyourself="/home/whitis/bin/echoargc"

Los dos lanzamientos de la GUI descritos anteriormente también encuentran correctamente el programa.

Hay una trampa potencial. La función access() deja caer los permisos si el programa está configurado 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 regular, puede haber una situación en la que estas pruebas fallarían, aunque es poco probable que el programa se pueda ejecutar en esas circunstancias. Uno podría usar euidaccess () en su lugar. Sin embargo, es posible que encuentre un programa inaccesible antes en el camino que el usuario real.


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. También ps es una utilidad POSIX por lo que debe ser portátil

entonces, si la identificación del proceso es 249297, este comando le da el nombre de la ruta solamente.

ps -p 24297 -o comm --no-heading

Explicación de argumentos

-p - selecciona un proceso dado

-o comm: muestra el nombre del comando (-o cmd selecciona toda la línea de comando)

--no-heading - no muestra una línea de encabezado, solo la salida.

El programa AC puede ejecutar esto vía popen.


Hacer que esto funcione de manera confiable en todas las plataformas requiere el uso de sentencias #ifdef.

El código siguiente encuentra la ruta del ejecutable en Windows, Linux, MacOS, Solaris o FreeBSD (aunque FreeBSD no se ha probado). Utiliza boost > = 1.55.0 para simplificar el código, pero es bastante fácil de eliminar si lo desea. Solo use define como _MSC_VER y __linux como lo requieren el sistema operativo y el compilador.

#include <string> #include <boost/predef/os.h> #if (BOOST_OS_WINDOWS) # include <stdlib.h> #elif (BOOST_OS_SOLARIS) # include <stdlib.h> # include <limits.h> #elif (BOOST_OS_LINUX) # include <unistd.h> # include <limits.h> #elif (BOOST_OS_MACOS) # include <mach-o/dyld.h> #elif (BOOST_OS_BSD_FREE) # include <sys/types.h> # include <sys/sysctl.h> #endif /* * Returns the full path to the currently running executable, * or an empty string in case of failure. */ std::string getExecutablePath() { #if (BOOST_OS_WINDOWS) char *exePath; if (_get_pgmptr(&exePath) != 0) exePath = ""; #elif (BOOST_OS_SOLARIS) char exePath[PATH_MAX]; if (realpath(getexecname(), exePath) == NULL) exePath[0] = ''/0''; #elif (BOOST_OS_LINUX) char exePath[PATH_MAX]; ssize_t len = ::readlink("/proc/self/exe", exePath, sizeof(exePath)); if (len == -1 || len == sizeof(exePath)) len = 0; exePath[len] = ''/0''; #elif (BOOST_OS_MACOS) char exePath[PATH_MAX]; uint32_t len = sizeof(exePath); if (_NSGetExecutablePath(exePath, &len) != 0) { exePath[0] = ''/0''; // buffer too small (!) } else { // resolve symlinks, ., .. if possible char *canonicalPath = realpath(exePath, NULL); if (canonicalPath != NULL) { strncpy(exePath,canonicalPath,len); free(canonicalPath); } } #elif (BOOST_OS_BSD_FREE) char exePath[2048]; int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PATHNAME; mib[3] = -1; size_t len = sizeof(exePath); if (sysctl(mib, 4, exePath, &len, NULL, 0) != 0) exePath[0] = ''/0''; #endif return std::string(exePath); }

La versión anterior devuelve rutas de acceso completas, incluido el nombre del ejecutable. Si, en cambio, desea la ruta sin el nombre del ejecutable, #include boost/filesystem.hpp> y cambie la declaración de devolución a:

return strlen(exePath)>0 ? boost::filesystem::path(exePath).remove_filename().make_preferred().string() : std::string();



Si usa C, puede usar la función getwd:

int main() { char buf[4096]; getwd(buf); printf(buf); }

Esto imprimirá en la salida estándar, el directorio actual del ejecutable.


Una alternativa en Linux para usar /proc/self/exe o argv[0] es usar la información pasada por el intérprete ELF, puesto a disposición por glibc como tal:

#include <stdio.h> #include <sys/auxv.h> int main(int argc, char **argv) { printf("%s/n", (char *)getauxval(AT_EXECFN)); return(0); }

Tenga en cuenta que getauxval es una extensión glibc, y para ser robusto debe verificar para que no devuelva NULL (lo que indica que el intérprete ELF no ha proporcionado el parámetro AT_EXECFN ), pero no creo que esto sea realmente un problema en Linux.


La ruta de valor absoluto de un programa está en la PWD del envp de su función principal, también hay una función en C llamada getenv, así que ahí está.