¿Cómo obtengo el directorio desde el que se ejecuta un programa?

269

¿Existe un método independiente de la plataforma y del sistema de archivos para obtener la ruta completa del directorio desde donde se ejecuta un programa usando C / C ++? No debe confundirse con el directorio de trabajo actual. (No sugiera bibliotecas a menos que sean estándar como clib o STL).

(Si no hay un método independiente de plataforma / sistema de archivos, las sugerencias que funcionan en Windows y Linux para sistemas de archivos específicos también son bienvenidas).

Ashwin Nanjappa
fuente
@chakrit: Eso sería genial. (Aunque ese problema no suele aparecer en Windows).
Ashwin Nanjappa
2
A menos que pueda extraer la ruta de manera confiable argv[0], la técnica dependerá mucho del sistema operativo.
David R Tribble
1
Solo para aclarar: el 'directorio actual' o 'el directorio desde el que se ejecuta el programa' (en la terminología de la pregunta) es el directorio donde se encuentra el archivo de imagen del programa (archivo ~ .exe), y el ' directorio de trabajo actual ' es el directorio, que se completa automáticamente si el programa usa rutas relativas?
colemik
3
Cuando usted #include <windows.h>, Windows coloca automáticamente una char*ruta ejecutable _pgmptr. No necesita llamar a funciones adicionales o asumir basura si solo está trabajando en Windows.
rsethc
1
Aunque el comentario es de hace tres años, me gustaría ampliar el comentario de rsethc sobre _pgmptr. La documentación de MSDN indica que las variables _pgmptry _wpgmptrestán en desuso, y debe usar la función _get_pgmptr(char**)o en su _get_wpgmptr(wchar_t**)lugar. MSDN
Hydranix

Respuestas:

181

Aquí hay un código para obtener la ruta completa a la aplicación en ejecución:

Ventanas:

int bytes = GetModuleFileName(NULL, pBuf, len);
return bytes ? bytes : -1;

Linux:

int bytes = MIN(readlink("/proc/self/exe", pBuf, len), len - 1);
if(bytes >= 0)
    pBuf[bytes] = '\0';
return bytes;
Deduplicador
fuente
3
Creo que esta es la única respuesta aquí que responde a la pregunta, y lo hace tanto para Windows como para Linux. Buen trabajo.
Frank Szczerba
66
Boo for / proc / pid / exe: por alguna razón, no es compatible con OS X.
Chris Lutz el
24
Cuando veo un código que mira una /procparte de mí, muere un poco. Todo el mundo no es Linux, e incluso en una plataforma que /procse debe considerar sujetos al cambio de versión en versión, arco a arco, etc
asveikau
44
si se inician utilizando un comando con alias en Linux, ¿es argv [0] el "nombre del comando" o expandido?
Andy Dent
20
¿Qué tal agregar char pBuf[256]; size_t len = sizeof(pBuf);para dejar la solución más clara.
charles.cc.hsu
166

Si obtiene el directorio actual cuando se inicia su programa, entonces efectivamente tiene el directorio desde el que se inició su programa. Almacene el valor en una variable y consúltelo más adelante en su programa. Esto es distinto del directorio que contiene el archivo de programa ejecutable actual . No es necesariamente el mismo directorio; si alguien ejecuta el programa desde un símbolo del sistema, el programa se ejecuta desde el directorio de trabajo actual del símbolo del sistema aunque el archivo del programa se encuentre en otro lugar.

getcwd es una función POSIX y es compatible con todas las plataformas compatibles con POSIX. No tendría que hacer nada especial (aparte de inclinar los encabezados correctos unistd.h en Unix y direct.h en windows).

Como está creando un programa en C, se vinculará con la biblioteca de tiempo de ejecución de c predeterminada a la que están vinculados TODOS los procesos en el sistema (se evitan las excepciones especialmente diseñadas) e incluirá esta función de manera predeterminada. El CRT nunca se considera una biblioteca externa porque proporciona la interfaz básica compatible con el sistema operativo.

En Windows, la función getcwd ha quedado en desuso a favor de _getcwd. Creo que podrías usarlo de esta manera.

#include <stdio.h>  /* defines FILENAME_MAX */
#ifdef WINDOWS
    #include <direct.h>
    #define GetCurrentDir _getcwd
#else
    #include <unistd.h>
    #define GetCurrentDir getcwd
 #endif

 char cCurrentPath[FILENAME_MAX];

 if (!GetCurrentDir(cCurrentPath, sizeof(cCurrentPath)))
     {
     return errno;
     }

cCurrentPath[sizeof(cCurrentPath) - 1] = '\0'; /* not really required */

printf ("The current working directory is %s", cCurrentPath);
informática
fuente
44
Buena respuesta, pero pensé que "directorio de trabajo actual" no era lo que quería.
Michael Burr
44
debe agregar que incluso si algunas documentaciones dicen que cCurrentpath puede ser nulo y será asignado por getcwd getcwd no parece asignar algo en Mac OS y bloquea silenciosamente su programa
Janusz
44
Hay un pequeño error, pero desafortunadamente no puedo editar aún ... línea 10: cCurrentpath: debería ser cCurrentPath
Lipis
8
OMI en Windows, las funciones con nombre POSIXy (algunas de las cuales comienzan con guiones bajos) generalmente deben evitarse. No son las API reales de Windows sino la CRT. La API de Windows que desea usar es GetCurrentDirectory (). msdn.microsoft.com/en-us/library/aa364934(VS.85).aspx
asveikau
66
La respuesta de Mike es correcta. El "directorio actual" no siempre es el mismo que el directorio desde el que se ejecuta el binario. Por ejemplo, si una aplicación se ejecuta como un servicio en Windows, el directorio actual probablemente será C: \ Windows \ System32, mientras que el directorio binario es diferente.
Lucky Luke
42

Esto es del foro cplusplus

En windows:

#include <string>
#include <windows.h>

std::string getexepath()
{
  char result[ MAX_PATH ];
  return std::string( result, GetModuleFileName( NULL, result, MAX_PATH ) );
}

En Linux:

#include <string>
#include <limits.h>
#include <unistd.h>

std::string getexepath()
{
  char result[ PATH_MAX ];
  ssize_t count = readlink( "/proc/self/exe", result, PATH_MAX );
  return std::string( result, (count > 0) ? count : 0 );
}

En HP-UX:

#include <string>
#include <limits.h>
#define _PSTAT64
#include <sys/pstat.h>
#include <sys/types.h>
#include <unistd.h>

std::string getexepath()
{
  char result[ PATH_MAX ];
  struct pst_status ps;

  if (pstat_getproc( &ps, sizeof( ps ), 0, getpid() ) < 0)
    return std::string();

  if (pstat_getpathname( result, PATH_MAX, &ps.pst_fid_text ) < 0)
    return std::string();

  return std::string( result );
}
Pulpo
fuente
1
Esa solución de Windows no manejará caracteres no ANSI en la ruta. Probablemente debería usar GetModuleFileNameW y convertirlo a UTF-8 explícitamente (teniendo cuidado de volver a convertirlo cada vez que necesite emitir un comando del sistema de archivos).
Adrian McCarthy
3
Para la solución de Windows, obtengo el error error: cannot convert 'char*' to 'LPWCH {aka wchar_t*}' for argument '2' to 'DWORD GetModuleFileNameW(HMODULE, LPWCH, DWORD)'al compilar con MinGW.
HelloGoodbye
2
@ Adrian, generalmente no soy un programador de Windows, pero ¿no hay un DEFINE o alguna forma de decirle a tu compilador que use el sabor _W () de las funciones automáticamente?
Pulpo
1
@Octopus: para usar las llamadas anchas, necesitaría usar WCHAR (en lugar de char) y std :: wstring (en lugar de std :: string).
Adrian McCarthy
29

Si desea una forma estándar sin bibliotecas: No. El concepto completo de un directorio no está incluido en el estándar.

Si está de acuerdo en que una dependencia (portátil) de una lib casi estándar está bien: use la biblioteca del sistema de archivos de Boost y solicite initial_path () .

En mi humilde opinión eso es lo más cerca que puede llegar, con buen karma (Boost es un conjunto de bibliotecas de alta calidad bien establecido)

Thorsten79
fuente
8
Desde los documentos de Boost: template <class Path> const Path & initial_path (); Devuelve: current_path () en el momento de la entrada a main (). Y current_path () es 'como si fuera POSIX getcwd ()'. Esto no es lo que solicitó el interlocutor.
Jonathan Leffler
ver boost.org/doc/libs/1_46_1/libs/filesystem/v3/doc/… para el impulso 1.46.1
moala
Como se comentó, esto proporciona la ruta desde donde se invocó el binario, no la ruta al binario ... ya que podría iniciarse desde una carpeta diferente.
jpo38
21

El sistema de archivos TS ahora es un estándar (y es compatible con gcc 5.3+ y clang 3.9+), por lo que puede usar la current_path()función de él:

std::string path = std::experimental::filesystem::current_path();

En gcc (5.3+) para incluir el sistema de archivos que necesita usar:

#include <experimental/filesystem>

y vincule su código con la -lstdc++fsbandera.

Si desea utilizar el Sistema de archivos con Microsoft Visual Studio, lea esto .

Marqin
fuente
66
Desde el enlace 1-2) Returns the absolute path of the current working directory, obtained as if by POSIX getcwd. (2) returns path() if error occurs. referenciado , Downvoted, ya que el OP pregunta específicamente sobre la ruta actual del ejecutable en lugar del directorio de trabajo actual.
S. Saad
20

Sé que es muy tarde en el día para dar una respuesta a esta, pero descubrí que ninguna de las respuestas fue tan útil para mí como mi propia solución. Una forma muy simple de obtener la ruta de su CWD a su carpeta bin es la siguiente:

int main(int argc, char* argv[])
{
    std::string argv_str(argv[0]);
    std::string base = argv_str.substr(0, argv_str.find_last_of("/"));
}

Ahora puede usar esto como base para su ruta relativa. Entonces, por ejemplo, tengo esta estructura de directorio:

main
  ----> test
  ----> src
  ----> bin

y quiero compilar mi código fuente en bin y escribir un registro para probar, solo puedo agregar esta línea a mi código.

std::string pathToWrite = base + "/../test/test.log";

He intentado este enfoque en Linux usando la ruta completa, alias, etc. y funciona bien.

NOTA:

Si está en Windows, debe usar un '\' como separador de archivos, no '/'. Tendrás que escapar de esto también, por ejemplo:

std::string base = argv[0].substr(0, argv[0].find_last_of("\\"));

Creo que esto debería funcionar, pero no lo he probado, por lo que se agradecería comentar si funciona o una solución si no.

Sam Redway
fuente
Sí, también funciona en Windows. Creo que esa es la mejor solución. Hasta donde yo sé argv [0] siempre mantiene la ruta al ejecutable.
Wodzu
44
argv[0]es una muy buena idea, pero desafortunadamente lo que obtengo en Linux es "./my_executable_name" o "./make/my_executable_name". Básicamente, lo que obtengo depende completamente de cómo lo
lanzo
1
@ Xeverous: ¿y qué? Si tengo algunos archivos relativos a mi ejecutable que necesita abrir, comenzar en "./" o "./make/" en sus casos debería funcionar. "." es el directorio de trabajo actual, y argv [0] le indicará la ruta relativa al ejecutable desde allí, que es exactamente lo que el OP debería desear. En cualquier caso, eso es exactamente lo que necesito.
nilo
9

No, no hay una forma estándar. Creo que los estándares C / C ++ ni siquiera consideran la existencia de directorios (u otras organizaciones de sistemas de archivos).

En Windows, GetModuleFileName () devolverá la ruta completa al archivo ejecutable del proceso actual cuando el parámetro hModule se establece en NULL . No puedo ayudar con Linux.

También debe aclarar si desea el directorio actual o el directorio donde reside la imagen / ejecutable del programa. Tal como está, su pregunta es un poco ambigua en este punto.

Michael Burr
fuente
9

En Windows, la forma más sencilla es utilizar la _get_pgmptrfunción stdlib.hpara obtener un puntero a una cadena que represente la ruta absoluta al ejecutable, incluido el nombre del ejecutable.

char* path;
_get_pgmptr(&path);
printf(path); // Example output: C:/Projects/Hello/World.exe
Adam Yaxley
fuente
8

¿Quizás concatenar el directorio de trabajo actual con argv [0]? No estoy seguro de si eso funcionaría en Windows, pero funciona en Linux.

Por ejemplo:

#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char **argv) {
    char the_path[256];

    getcwd(the_path, 255);
    strcat(the_path, "/");
    strcat(the_path, argv[0]);

    printf("%s\n", the_path);

    return 0;
}

Cuando se ejecuta, genera:

jeremy @ jeremy-desktop: ~ / Desktop $ ./test
/home/jeremy/Desktop/./test

Jeremy Ruten
fuente
Necesitará una comprobación para ver si se proporciona una ruta absoluta en argv [0]. Pero lo más importante, ¿qué pasa si la imagen se encuentra a través de la RUTA? ¿Linux completa la ruta completa o simplemente lo que está en la línea de comando?
Michael Burr
Como señaló Mike B, esa es una solución no general; funciona solo en algunas circunstancias muy limitadas. Básicamente, solo cuando ejecuta el comando por un nombre de ruta relativo, y no es tan elegante cuando ejecuta ../../../bin/progname en lugar de ./test
Jonathan Leffler
Si resuelve la posible ruta relativa de argv [0] en comparación con el directorio actual (porque argv [0] podría ser "../../myprogram.exe"), esa es probablemente la forma más segura de responder la pregunta. Siempre funcionará y es portátil (¡incluso funciona en Android!).
jpo38
7

Para Win32 GetCurrentDirectory debería hacer el truco.

Torbjörn Gyllebring
fuente
Hay un gran inconveniente con esta función : las aplicaciones multiproceso y el código de biblioteca compartida no deben usar la función GetCurrentDirectory y deben evitar el uso de nombres de ruta relativos . Si puede trabajar con esta suposición, entonces es la mejor solución.
McLeary
6

No puede usar argv [0] para ese propósito, generalmente contiene la ruta completa al ejecutable, pero no necesariamente; el proceso podría crearse con un valor arbitrario en el campo.

También tenga en cuenta que el directorio actual y el directorio con el ejecutable son dos cosas diferentes, por lo que getcwd () tampoco lo ayudará.

En Windows, use GetModuleFileName (), en los archivos read / dev / proc / procID / .. de Linux .

Eugensk
fuente
3

Solo para atrasarme aquí, ...

no existe una solución estándar, porque los lenguajes son independientes de los sistemas de archivos subyacentes, por lo que, como otros han dicho, el concepto de un sistema de archivos basado en directorios está fuera del alcance de los lenguajes c / c ++.

Además de eso, no desea el directorio de trabajo actual, sino el directorio en el que se ejecuta el programa, que debe tener en cuenta cómo llegó el programa a donde está, es decir, se generó como un nuevo proceso a través de un tenedor, etc. Para obtener el directorio en el que se ejecuta un programa, como lo han demostrado las soluciones, es necesario que obtenga esa información de las estructuras de control de procesos del sistema operativo en cuestión, que es la única autoridad en esta cuestión. Por lo tanto, por definición, es una solución específica del sistema operativo.

Minok
fuente
3

Para el sistema Windows en la consola, puede usar el dircomando system ( ). Y la consola le brinda información sobre el directorio, etc. Lea sobre el dircomando en cmd. Pero para sistemas tipo Unix, no sé ... Si se ejecuta este comando, lea el comando bash. lsno muestra el directorio ...

Ejemplo:

int main()
{
    system("dir");
    system("pause"); //this wait for Enter-key-press;
    return 0;
}
Alexey1993
fuente
2
#include <windows.h>
using namespace std;

// The directory path returned by native GetCurrentDirectory() no end backslash
string getCurrentDirectoryOnWindows()
{
    const unsigned long maxDir = 260;
    char currentDir[maxDir];
    GetCurrentDirectory(maxDir, currentDir);
    return string(currentDir);
}
freezotic
fuente
1

En las plataformas POSIX, puede usar getcwd () .

En Windows, puede usar _getcwd () , ya que el uso de getcwd () ha quedado en desuso.

Para las bibliotecas estándar, si Boost fuera lo suficientemente estándar para usted, le habría sugerido el sistema de archivos Boost ::, pero parecen haber eliminado la normalización de ruta de la propuesta. Puede que tenga que esperar hasta que TR2 esté disponible para una solución completamente estándar.

Fruny
fuente
10
getcwd () no hace lo que le preguntó el interrogador.
Jonathan Leffler
¿No es que la respuesta aceptada usa getcwd (), o no estoy entendiendo?
Sanađошƒаӽ
He votado porque tú eres el que vino con lo que se considera la respuesta correcta primero.
Arnaud
Esta respuesta ni siquiera intenta abordar la pregunta. Es una pena escribirlo.
HelloWorld
1

Para caminos relativos, esto es lo que hice. Soy consciente de la antigüedad de esta pregunta, simplemente quiero aportar una respuesta más simple que funcione en la mayoría de los casos:

Digamos que tienes un camino como este:

"path/to/file/folder"

Por alguna razón, los ejecutables creados con Linux hechos en eclipse funcionan bien con esto. Sin embargo, Windows se confunde mucho si se le da una ruta como esta para trabajar.

Como se indicó anteriormente, hay varias formas de obtener la ruta actual al ejecutable, pero la forma más fácil que encuentro funciona de maravilla en la mayoría de los casos es agregar esto al FRENTE de su ruta:

"./path/to/file/folder"

¡Solo agregar "./" debería ordenarlo! :) Entonces puede comenzar a cargar desde el directorio que desee, siempre que sea con el ejecutable mismo.

EDITAR: Esto no funcionará si intenta iniciar el ejecutable desde code :: blocks si ese es el entorno de desarrollo que se está utilizando, ya que por alguna razón, code :: blocks no carga las cosas correctamente ...: D

EDIT2: Algunas cosas nuevas que he encontrado es que si especifica una ruta estática como esta en su código (Suponiendo que Example.data es algo que necesita cargar):

"resources/Example.data"

Si luego inicia su aplicación desde el directorio real (o en Windows, crea un acceso directo y establece el directorio de trabajo en el directorio de la aplicación), funcionará así. Tenga esto en cuenta al depurar problemas relacionados con la falta de rutas de recursos / archivos. (Especialmente en IDE que establecen el directorio de trabajo incorrecto al iniciar un exe de compilación desde el IDE)

FuzzyQuills
fuente
1

Una solución de biblioteca (aunque sé que esto no fue solicitado). Si usa Qt: QCoreApplication::applicationDirPath()

Joaquín
fuente
1

Solo mis dos centavos, pero ¿el siguiente código no funciona de manera portátil en C ++ 17?

#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;

int main(int argc, char* argv[])
{
    std::cout << "Path is " << fs::path(argv[0]).parent_path() << '\n';
}

Parece que funciona para mí en Linux al menos.

Basado en la idea anterior, ahora tengo:

std::filesystem::path prepend_exe_path(const std::string& filename, const std::string& exe_path = "");

Con implementación:

fs::path prepend_exe_path(const std::string& filename, const std::string& exe_path)
{
    static auto exe_parent_path = fs::path(exe_path).parent_path();
    return exe_parent_path / filename;
}

Y truco de inicialización en main():

(void) prepend_exe_path("", argv[0]);

Gracias @Sam Redway por la idea argv [0]. Y, por supuesto, entiendo que C ++ 17 no existió durante muchos años cuando el OP hizo la pregunta.

nilo
fuente
0

Boost Filesystem se initial_path()comporta como POSIX getcwd(), y tampoco hace lo que quiere por sí mismo, pero agregarlo argv[0]a cualquiera de ellos debería hacerlo.

Puede notar que el resultado no siempre es bonito: puede obtener cosas como /foo/bar/../../baz/a.outo /foo/bar//baz/a.out, pero creo que siempre da como resultado una ruta válida que nombra el ejecutable (tenga en cuenta que las barras diagonales consecutivas en una ruta se colapsan en una).

Anteriormente escribí una solución usando envp(el tercer argumento en el main()que funcionó en Linux pero no parecía viable en Windows, por lo que esencialmente estoy recomendando la misma solución que otra persona lo hizo anteriormente, pero con la explicación adicional de por qué es realmente correcto incluso si los resultados no son bonitos.

John Zwinck
fuente
0

Como mencionó Minok , no hay tal funcionalidad especificada en el estándar C ni en el estándar C ++. Esto se considera una característica puramente específica del sistema operativo y se especifica en el estándar POSIX, por ejemplo.

Thorsten79 ha dado una buena sugerencia, es la biblioteca Boost.Filesystem. Sin embargo, puede ser inconveniente en caso de que no desee tener dependencias de tiempo de enlace en forma binaria para su programa.

Una buena alternativa que recomendaría es la colección de bibliotecas STLSoft C ++ 100% solo con encabezados Matthew Wilson (autor de libros de lectura obligada sobre C ++). Hay una fachada portátil. PlatformSTL da acceso a la API específica del sistema: WinSTL para Windows y UnixSTL en Unix, por lo que es una solución portátil. Todos los elementos específicos del sistema se especifican con el uso de rasgos y políticas, por lo que es un marco extensible. Hay una biblioteca de sistema de archivos proporcionada, por supuesto.

mloskot
fuente
0

El comando linux bash cuyo nombre de programa informará una ruta al programa.

Incluso si uno pudiera emitir el comando qué desde su programa y dirigir la salida a un archivo tmp y el programa posteriormente lee ese archivo tmp, no le dirá si ese programa es el que se está ejecutando. Solo le dice dónde se encuentra un programa con ese nombre.

Lo que se requiere es obtener su número de identificación de proceso y analizar la ruta al nombre

En mi programa, quiero saber si el programa se ejecutó desde el directorio bin del usuario o desde otro en la ruta o desde / usr / bin. / usr / bin contendría la versión compatible. Mi sensación es que en Linux existe una solución que es portátil.

Leslie Satenstein
fuente
0

Utilizar realpath()en stdlib.heste aspecto:

char *working_dir_path = realpath(".", NULL);
Manabu Nakazawa
fuente
0

Funciona comenzando desde C ++ 11, usando un sistema de archivos experimental, y C ++ 14-C ++ 17 también usando un sistema de archivos oficial.

aplicación.h:

#pragma once

//
// https://en.cppreference.com/w/User:D41D8CD98F/feature_testing_macros
//
#ifdef __cpp_lib_filesystem
#include <filesystem>
#else
#include <experimental/filesystem>

namespace std {
    namespace filesystem = experimental::filesystem;
}
#endif

std::filesystem::path getexepath();

application.cpp:

#include "application.h"
#ifdef _WIN32
#include <windows.h>    //GetModuleFileNameW
#else
#include <limits.h>
#include <unistd.h>     //readlink
#endif

std::filesystem::path getexepath()
{
#ifdef _WIN32
    wchar_t path[MAX_PATH] = { 0 };
    GetModuleFileNameW(NULL, path, MAX_PATH);
    return path;
#else
    char result[PATH_MAX];
    ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
    return std::string(result, (count > 0) ? count : 0);
#endif
}
TarmoPikaro
fuente
Buena respuesta, pero es un comportamiento indefinido agregar declaraciones o definiciones al espacio de nombresstd . Para evitar esto, puede agregar los espacios de nombres std::filesystemy std::experimental::filesystemun tercer espacio de nombres de su elección, o simplemente usar using std::filesystem::path, si no le importa agregar la declaración pathal espacio de nombres global.
Cássio Renan
Supongo que después de C ++ 14 experimental :: el sistema de archivos ya no se usa, ¿entonces puedes olvidarte de esto? (entra en la primera rama #if)
TarmoPikaro