“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?
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.
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.
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.
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 .
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).
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].
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.
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.
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.
execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);
. O nome do executável não tem relação com o valor emargv[0]
.Respostas:
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):
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:
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 aexec
família de chamadas, tem de (e faz) documento lo.fonte
argv[0]
é pertinente à programação no mundo real.Em
*nix
sistemas de tipo comexec*()
chamadas,argv[0]
será o que o chamador colocar noargv0
lugar daexec*()
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 fazerargv[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.fonte
De acordo com o padrão C ++, seção 3.6.1:
Então não, não é garantido, pelo menos pela Norma.
fonte
ISO-IEC 9899 afirma:
Eu também usei:
E então você só precisa analisar a string para extrair o nome do executável do caminho.
fonte
/proc/self/path/a.out
link simbólico pode ser usado no Solaris 10 e superior.GetModuleFileNameW
deve ser usado para ser capaz de recuperar qualquer caminho, mas apenas a presença do código constitui uma boa orientação).Esta página afirma:
No entanto, outras páginas parecem apoiar o fato de que é sempre o nome do executável. Este afirma:
fonte
argv[0]="-/bin/sh"
? De qualquer forma, esse é o caso de todas as máquinas que usei.Aplicativos com
argv[0] !=
nome executávelmuitos 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
getty
que adiciona o líder-
, consulte também: /unix/299408/how-to-login-automatically-without-typing-the-root-username-or-password -in-build / 300152 # 300152binários de multi-chamada, talvez mais notavelmente Busybox . Esses símbolos vinculam vários nomes, por exemplo,
/bin/sh
e/bin/ls
a um único executável/bin/busybox
, que reconhece de qual ferramenta usarargv[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
execve
Exemplo de POSIXargv[0] !=
executável em que o nome do executávelOutros mencionados
exec
, mas aqui está um exemplo executável.ac
aC
Então:
Dá:
Sim,
argv[0]
também pode ser:Testado em Ubuntu 16.10.
fonte
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.
fonte
Se você iniciar um programa Amiga pelo Workbench, argv [0] não será definido, apenas pela CLI.
fonte