“Argv [0] = nome-do-executável” é um padrão aceito ou apenas uma convenção comum?

102

Ao passar o argumento para main()em um aplicativo C ou C ++, argv[0]sempre será o nome do executável? Ou isso é apenas uma convenção comum e não é garantido que seja verdade 100% do tempo?

Mike Willekes
fonte
19
No Unix, considere: execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);. O nome do executável não tem relação com o valor em argv[0].
Jonathan Leffler

Respostas:

118

O trabalho de adivinhação (até mesmo adivinhação educada) é divertido, mas você realmente precisa consultar os documentos de padrões para ter certeza. Por exemplo, ISO C11 afirma (grifo meu):

Se o valor de argcfor maior que zero, a string apontada por argv[0] representa o nome do programa; argv[0][0]deve ser o caractere nulo se o nome do programa não estiver disponível no ambiente do host.

Portanto, não, é apenas o nome do programa, se esse nome estiver disponível. E "representa" o nome do programa, não necessariamente é o nome do programa. A seção anterior afirma:

Se o valor de argcfor maior que zero, os membros da matriz argv[0]até argv[argc-1]inclusive devem conter ponteiros para strings, que recebem valores definidos pela implementação pelo ambiente host antes da inicialização do programa.

Isso não foi alterado em relação ao C99, o padrão anterior, e significa que mesmo os valores não são ditados pelo padrão - depende inteiramente da implementação.

Isto significa que o nome do programa pode estar vazio se o ambiente de host não fornecê-la, e qualquer outra coisa, se o ambiente de acolhimento faz fornecer, desde que "qualquer outra coisa" represente de alguma forma o nome do programa. Em meus momentos mais sádicos, consideraria traduzi-lo para o suaíli, executá-lo por meio de uma cifra de substituição e armazená-lo na ordem reversa dos bytes :-).

No entanto, a implementação definida não tem um significado específico nas normas ISO - o documento de implementação deve como ele funciona. Assim, mesmo UNIX, o que pode colocar qualquer coisa que ele gosta em argv[0]com a execfamília de chamadas, tem de (e faz) documento lo.

paxdiablo
fonte
3
Esse pode ser o padrão, mas o Unix simplesmente não o impõe e você não pode contar com isso.
dmckee --- ex-moderador gatinho
4
A pergunta não mencionou o UNIX em tudo . Era uma questão C pura e simples, portanto ISO C é o documento de referência. O nome do programa é implementação definida no padrão, portanto, uma implementação é livre para fazer o que quiser, incluindo permitir algo lá que não seja o nome real - pensei ter deixado isso claro na penúltima frase.
paxdiablo
2
Pax, eu não votei em você, e não aprovo aqueles que votaram porque esta resposta é a mais confiável que pode ser. Mas acho que a falta de confiabilidade do valor de argv[0]é pertinente à programação no mundo real.
dmckee --- ex-moderador gatinho
4
@caf, correto. Eu o vi segurando coisas tão diversas como o caminho completo do programa ('/ progpath / prog'), apenas o nome do arquivo ('prog'), um nome ligeiramente modificado ('-prog'), um nome descritivo (' prog - um programa para progging ') e nada (' '). A implementação deve definir o que contém, mas isso é tudo que o padrão exige.
paxdiablo
3
Obrigado a todos! Grande discussão de uma questão (aparentemente) simples. Embora a resposta de Richard seja válida para sistemas operacionais * nix, escolhi a resposta de paxdiablo porque estou menos interessado no comportamento de um sistema operacional específico e principalmente na existência (ou ausência de) um padrão aceito. (Se você estiver curioso: no contexto da pergunta original - eu não tenho sistema operacional. Estou escrevendo o código para construir o buffer argc / argv bruto para um executável carregado em um dispositivo incorporado e precisava saber o que devo fazer com argv [0]). +1 para StackOverflow por ser incrível!
Mike Willekes
48

Em *nixsistemas de tipo com exec*()chamadas, argv[0]será o que o chamador colocar no argv0lugar da exec*()chamada.

O shell usa a convenção de que este é o nome do programa, e a maioria dos outros programas segue a mesma convenção, então argv[0]geralmente o nome do programa.

Mas um programa Unix desonesto pode chamar exec()e fazer argv[0]qualquer coisa que quiser, então não importa o que o padrão C diga, você não pode contar com isso 100% do tempo.

Richard Pennington
fonte
4
Esta é uma resposta melhor do que a do paxdiablo acima. O padrão apenas o chama de "nome do programa", mas isso não é aplicado em nenhum lugar que eu saiba. Os kernels Unix passam uniformemente a string passada para execve () inalterada para o processo filho.
Andy Ross
4
O padrão C é limitado no que pode dizer porque não sabe sobre 'execve ()' etc. O padrão POSIX ( opengroup.org/onlinepubs/9699919799/functions/execve.html ) tem mais a dizer - deixando isso claro que o que está em argv [0] está no capricho do processo que executa a chamada de sistema 'execve ()' (ou relacionada).
Jonathan Leffler
1
@Andy, você é livre para ter suas opiniões :-) Mas você está errado sobre a aplicação. Se uma implementação não seguir o padrão, ela não está em conformidade. E, de fato, uma vez que é definido pela implementação quanto ao que é o "nome do programa", um SO como o UNIX está em conformidade, desde que especifique qual é o nome. Isso inclui ser capaz de falsificar descaradamente um nome de programa, carregando argv [0] com qualquer coisa que você quiser na família de chamadas exec.
paxdiablo
Essa é a beleza da palavra "representa" no padrão quando se refere a argv [0] ("representa o nome do programa") e argv [1..N] ("eles representam os argumentos do programa"). "andorinha sem carga" é um nome de programa válido.
Richard Pennington
8

De acordo com o padrão C ++, seção 3.6.1:

argv [0] deve ser o ponteiro para o caractere inicial de um NTMBS que representa o nome usado para invocar o programa ou ""

Então não, não é garantido, pelo menos pela Norma.


fonte
5
Presumo que seja uma string de bytes múltiplos terminada em nulo?
paxdiablo
5

ISO-IEC 9899 afirma:

5.1.2.2.1 Inicialização do programa

Se o valor de argcfor maior que zero, a string apontada por argv[0]representa o nome do programa; argv[0][0]deve ser o caractere nulo se o nome do programa não estiver disponível no ambiente do host. Se o valor de argcfor maior que um, as strings apontadas por argv[1]através argv[argc-1]representam os parâmetros do programa .

Eu também usei:

#if defined(_WIN32)
  static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
  {
    return GetModuleFileNameA(NULL, pathName, (DWORD)pathNameCapacity);
  }
#elif defined(__linux__) /* elif of: #if defined(_WIN32) */
  #include <unistd.h>
  static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
  {
    size_t pathNameSize = readlink("/proc/self/exe", pathName, pathNameCapacity - 1);
    pathName[pathNameSize] = '\0';
    return pathNameSize;
  }
#elif defined(__APPLE__) /* elif of: #elif defined(__linux__) */
  #include <mach-o/dyld.h>
  static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
  {
    uint32_t pathNameSize = 0;

    _NSGetExecutablePath(NULL, &pathNameSize);

    if (pathNameSize > pathNameCapacity)
      pathNameSize = pathNameCapacity;

    if (!_NSGetExecutablePath(pathName, &pathNameSize))
    {
      char real[PATH_MAX];

      if (realpath(pathName, real) != NULL)
      {
        pathNameSize = strlen(real);
        strncpy(pathName, real, pathNameSize);
      }

      return pathNameSize;
    }

    return 0;
  }
#else /* else of: #elif defined(__APPLE__) */
  #error provide your own implementation
#endif /* end of: #if defined(_WIN32) */

E então você só precisa analisar a string para extrair o nome do executável do caminho.

Gregory Pakosz
fonte
2
O /proc/self/path/a.outlink simbólico pode ser usado no Solaris 10 e superior.
efemiente de
Aprovado para o código (sem dizer que é ideal ou correto, por exemplo, no Windows GetModuleFileNameWdeve ser usado para ser capaz de recuperar qualquer caminho, mas apenas a presença do código constitui uma boa orientação).
Saúde e hth. - Alf
4

Esta página afirma:

O elemento argv [0] normalmente contém o nome do programa, mas não se deve confiar nele - de qualquer forma, é incomum um programa não saber seu próprio nome!

No entanto, outras páginas parecem apoiar o fato de que é sempre o nome do executável. Este afirma:

Você notará que argv [0] é o caminho e o nome do próprio programa. Isso permite que o programa descubra informações sobre si mesmo. Ele também adiciona mais um ao array de argumentos do programa, portanto, um erro comum ao buscar argumentos de linha de comando é pegar argv [0] quando quiser argv [1].

ChrisF
fonte
11
Alguns programas se aproveitam do fato de não saberem o nome que foi usado para invocá-los. Acredito que BusyBox ( busybox.net/about.html ) funcione dessa maneira. Existe apenas um executável que implementa muitos utilitários de linha de comando diferentes. Ele usa um monte de links simbólicos e argv [0] para determinar qual ferramenta de linha de comando deve ser executada
Trent
Sim, lembro-me de notar que "gunzip" era um link simbólico para "gzip" e de me perguntar por um momento como isso funcionava.
David Thornley
2
Muitos programas procuram informações em argv [0]; por exemplo, se o último componente do nome começar com um travessão ('/ bin / -sh', por exemplo), o shell executará o perfil e outras coisas como um shell de login.
Jonathan Leffler
2
@Jon: Achei que os shells de login foram iniciados com argv[0]="-/bin/sh"? De qualquer forma, esse é o caso de todas as máquinas que usei.
efêmero de
3

Aplicativos com argv[0] !=nome executável

  • muitos shells determinam se são um shell de login verificando argv[0][0] == '-'. Os shells de login têm propriedades diferentes, principalmente por fornecerem alguns arquivos padrão, como /etc/profile.

    Normalmente é o próprio init ou gettyque adiciona o líder -, consulte também: /unix/299408/how-to-login-automatically-without-typing-the-root-username-or-password -in-build / 300152 # 300152

  • binários de multi-chamada, talvez mais notavelmente Busybox . Esses símbolos vinculam vários nomes, por exemplo, /bin/she /bin/lsa um único executável /bin/busybox, que reconhece de qual ferramenta usar argv[0].

    Isso torna possível ter um único pequeno executável estaticamente vinculado que representa várias ferramentas e funcionará basicamente em qualquer ambiente Linux.

Veja também: /unix/315812/why-does-argv-include-the-program-name/315817

execveExemplo de POSIX argv[0] !=executável em que o nome do executável

Outros mencionados exec, mas aqui está um exemplo executável.

ac

#define _XOPEN_SOURCE 700
#include <unistd.h>

int main(void) {
    char *argv[] = {"yada yada", NULL};
    char *envp[] = {NULL};
    execve("b.out", argv, envp);
}

aC

#include <stdio.h>

int main(int argc, char **argv) {
    puts(argv[0]);
}

Então:

gcc a.c -o a.out
gcc b.c -o b.out
./a.out

Dá:

yada yada

Sim, argv[0]também pode ser:

Testado em Ubuntu 16.10.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fonte
2

Não tenho certeza se é uma convenção quase universal ou um padrão, mas de qualquer forma você deve obedecê-la. Eu nunca vi isso explorado fora do Unix e sistemas semelhantes ao Unix, no entanto. Em ambientes Unix - e talvez particularmente nos velhos tempos - os programas podem ter comportamentos significativamente diferentes, dependendo do nome sob o qual são chamados.

EDITADO: Vejo em outros posts ao mesmo tempo que o meu que alguém o identificou como vindo de um padrão específico, mas tenho certeza de que a convenção é muito anterior ao padrão.

Joe Mabel
fonte
1
Eu realmente gostaria que, se as pessoas fossem "anotar" minha resposta, dessem alguma indicação do que não gostam nisso.
Joe Mabel
0

Se você iniciar um programa Amiga pelo Workbench, argv [0] não será definido, apenas pela CLI.

Polluks
fonte