Por que argv inclui o nome do programa?

Programas Unix / Linux típicos aceitam as entradas de linha de comando como uma contagem de argumento (int argc) e um vetor de argumento (char *argv[]). O primeiro elemento de argv é o nome do programa – seguido pelos argumentos reais.

Por que o nome do programa é passado para o executável como um argumento? Existem exemplos de programas que usam seus próprios nomes (talvez algum tipo de exec situação)?

Comentários

  • gosta de mv e cp?
  • No Debian, sh é um link simbólico para dash. Eles se comportam de maneira diferente, quando chamados de sh ou dash
  • @AlexejMagura Se você usar algo como busybox (comum em discos de resgate e outros), então praticamente tudo (cp, mv, rm, ls, …) é um link simbólico para o busybox.
  • Eu ‘ estou achando isso realmente difícil de ignorar, então ‘ ll diga: você provavelmente quer dizer ” GNU ” programas (gcc, bash, gunzip, a maior parte do restante do sistema operacional …), pois o Linux é apenas o kernel.
  • @ wizzwizz4 O que ‘ há de errado com ” Programas Unix / Linux típicos “? Eu li como ” Programas típicos em execução em Unix / Linux “. Isso ‘ é muito melhor do que sua restrição a certos programas GNU. Dennis Ritchie certamente não estava usando nenhum programa GNU. BTW, o kernel Hurd é um exemplo de programa GNU que não tem uma função principal …

Resposta

Para começar, observe que argv[0] não é necessariamente o nome do programa. É o que o chamador coloca em argv[0] da execve chamada do sistema (por exemplo, consulte esta pergunta no Stack Overflow ). (Todas as outras variantes de exec não são chamadas do sistema, mas interfaces para execve.)

Suponha, por exemplo, o seguinte (usando execl):

execl("/var/tmp/mybackdoor", "top", NULL); 

/var/tmp/mybackdoor é o que é executado, mas argv[0] é definido como top, e é isso que ps ou ( o real) top seria exibido. Veja esta resposta em U & L SE para mais informações.

Configurando todos isso à parte: Antes do advento de sistemas de arquivos sofisticados como /proc, argv[0] era a única maneira de um processo aprender sobre seu próprio nome. Para que isso seria bom?

  • Vários programas personalizam seu comportamento dependendo do nome pelo qual foram chamados (geralmente por links simbólicos ou físicos, por exemplo Utilitários do BusyBox “; vários outros exemplos são fornecidos em outras respostas a esta pergunta).
  • Além disso, serviços, daemons e outros programas que registram por meio do syslog geralmente adicionam seus nomes ao entradas de registro; sem isso, o rastreamento de eventos se tornaria quase inviável.

Comentários

  • Exemplos de tais programas são bunzip2, bzcat e bzip2, para os quais os dois primeiros são links simbólicos para o terceiro.
  • @Ruslan Curiosamente, zcat não é um link simbólico. Eles parecem evitar as desvantagens dessa técnica usando um script de shell. Mas eles não conseguem imprimir um saída porque alguém que adicionou opções ao gzip esqueceu de principal tain zcat também.
  • Desde que me lembro, os padrões de codificação GNU desencorajaram o uso de argv [0] para alterar o comportamento do programa (seção ” Padrões para interfaces geralmente ” na versão atual ). gunzip é uma exceção histórica.
  • busybox é outro excelente exemplo. Pode ser chamado por 308 nomes diferentes para chamar comandos diferentes: busybox.net/downloads/BusyBox.html#commands
  • Muitos, muitos mais programas também injetam seu argv[0] em sua saída de uso / ajuda em vez de codificar seu nome. Alguns completos, alguns apenas o nome de base.

Resposta

Bastante:

  • Bash é executado no modo POSIX quando argv[0] é sh. Ele é executado como um shell de login quando argv[0] começa com -.
  • O Vim se comporta de maneira diferente quando executado como vi, view, evim, eview, ex, vimdiff, etc.
  • Busybox, como já mencionado.
  • Em sistemas com systemd como init, shutdown, reboot, etc. são links simbólicos para systemctl .
  • e assim por diante.

Comentários

  • Outro é sendmail e mail. Cada MTA unix vem com um link simbólico para esses dois comandos e é projetado para emular o comportamento do ‘ original quando chamado como tal, o que significa que qualquer programa unix que precisa enviar e-mail sabe exatamente como eles podem fazer isso.
  • outro caso comum: test e [: quando você chama o primeiro , ele lida com um erro se o último argumento for ]. (no Debian estável real, esses comandos são dois programas diferentes, mas versões anteriores e MacOs ainda usam o mesmo programa). E tex, latex e assim por diante: o binário é o mesmo, mas olhando como foi chamado, ele escolhe o adequado arquivo de configuração . init é semelhante.
  • Relacionado, [ considera um erro se o último argumento não ].
  • Acho que isso responde à segunda pergunta, mas não à primeira. Duvido muito que algum designer de sistema operacional tenha se sentado e dito » Ei, seria legal se eu tivesse o mesmo programa fazendo coisas diferentes apenas com base em seu nome executável. Acho que ‘ vou incluir o nome em sua matriz de argumento, então. «
  • @Joey Sim, o o texto pretende transmitir que (Q: ” Existe algum …? ” A: ” Abundância: … “)

Resposta

Historicamente, argv é apenas uma matriz de ponteiros para as “palavras” da linha de comando, então faz sentido começar com a primeira “palavra”, que por acaso é a nome do programa.

E há alguns programas que se comportam de maneira diferente de acordo com o nome usado para chamá-los, então você pode simplesmente criar links diferentes para eles e obter “comandos” diferentes. o exemplo mais extremo que posso imaginar é o busybox , que age como várias dezenas de “comandos” diferentes, dependendo de como é chamado .

Editar

: Referências para Unix 1ª edição, conforme solicitado

Pode-se ver, por exemplo da função principal de cc que argc e argv já foram usados. O shell copia os argumentos para o parbuf dentro da newarg parte de o loop, enquanto trata o próprio comando da mesma maneira que os argumentos. (Claro, mais tarde ele executa apenas o primeiro argumento, que é o nome do comando). Parece que execv e parentes não existiam naquela época.

Comentários

  • por favor, adicione referências que faça backup disso.
  • Em uma rápida leitura, exec pega o nome do comando a ser executado e uma matriz terminada em zero de ponteiros de caracteres (melhor visto em minnie.tuhs.org/cgi-bin/utree.pl?file=V1/u0.s , onde exec leva referências ao rótulo 2 e rótulo 1, e no rótulo 2: aparece etc/init\0, e no rótulo 1: aparece como uma referência ao rótulo 2 e um zero final), que é basicamente o que execve faz hoje menos envp.
  • execv e execl existiram ” desde sempre ” (isto é, desde o início até meados dos anos 1970) – execv era uma chamada de sistema e foi uma função de biblioteca que o chamou. execve não ‘ não existia então porque o ambiente não ‘ então existia. Os outros membros da família foram adicionados mais tarde.
  • @ G-Man Você pode me apontar para execv na fonte v1 que vinculei? Apenas curiosidade.

Resposta

Casos de uso:

Você pode usar o nome do programa para alterar o comportamento do programa .

Por exemplo, você poderia criar alguns links simbólicos para o binário real.

Um exemplo famoso em que essa técnica é usada é o projeto busybox que instala apenas um binário único e muitos links simbólicos para ele. (ls, cp, mv, etc.). Eles estão fazendo isso para economizar espaço de armazenamento porque seus alvos são pequenos dispositivos incorporados.

Isso também é usado em setarch do util-linux:

$ ls -l /usr/bin/ | grep setarch lrwxrwxrwx 1 root root 7 2015-11-05 02:15 i386 -> setarch lrwxrwxrwx 1 root root 7 2015-11-05 02:15 linux32 -> setarch lrwxrwxrwx 1 root root 7 2015-11-05 02:15 linux64 -> setarch -rwxr-xr-x 1 root root 14680 2015-10-22 16:54 setarch lrwxrwxrwx 1 root root 7 2015-11-05 02:15 x86_64 -> setarch 

Aqui, eles estão usando essa técnica basicamente para evitar muitos arquivos de origem duplicados ou apenas para manter as fontes mais legíveis.

Outro caso de uso seria um programa que precisa para carregar alguns módulos ou dados em tempo de execução. Ter o caminho do programa torna você capaz de carregar módulos de um caminho relativo ao local do programa .

Além disso, muitos programas imprimem mensagens de erro incluindo o nome do programa .

Por que :

  1. Porque é uma convenção POSIX ( man 3p execve):

argv é uma matriz de strings de argumento passada para o novo programa. Por convenção, a primeira dessas strings deve conter o nome do arquivo associado ao arquivo que está sendo executado.

  1. É “C padrão (pelo menos C99 e C11):

Se o valor de argc for 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 host.

Observe que o padrão C diz “programa nome “não” nome do arquivo “.

Comentários

  • Não ‘ esta quebra se você alcançar o link simbólico de outro link simbólico?
  • @Mehrdad, Sim, isso ‘ é o lado negativo e pode ser confuso para o usuário.
  • @rudimeier: Seus ‘ Por que ‘ itens não são realmente razões, eles ‘ são apenas ” homunculus “, ou seja, apenas levanta a questão de por que o padrão exige que seja esse o caso.
  • @ A pergunta do einpoklum OP ‘ era: Por que o nome do programa é passado para o executável? Eu respondi: Porque POSIX e o padrão C nos dizem para fazer isso. Como você acha que ‘ não é realmente um motivo ? Se os documentos que eu ‘ citei não existissem, provavelmente muitos programas não passariam o nome do programa.
  • O OP está efetivamente pedindo ” POR QUE os padrões POSIX e C dizem para fazer isso? ” Concedido que o texto estava em um nível abstrato, mas parece claro. Realisticamente, a única maneira de saber é perguntar aos criadores.

Resposta

Além de programas alterando seus comportamento dependendo de como eles foram chamados, acho argv[0] útil para imprimir o uso de um programa, assim:

printf("Usage: %s [arguments]\n", argv[0]); 

Isso faz com que a mensagem de uso sempre use o nome pelo qual foi chamada. Se o programa for renomeado, sua mensagem de uso mudará com ele. Inclui até mesmo o nome do caminho com o qual foi chamado:

# cat foo.c #include <stdio.h> int main(int argc, char **argv) { printf("Usage: %s [arguments]\n", argv[0]); } # gcc -Wall -o foo foo.c # mv foo /usr/bin # cd /usr/bin # ln -s foo bar # foo Usage: foo [arguments] # bar Usage: bar [arguments] # ./foo Usage: ./foo [arguments] # /usr/bin/foo Usage: /usr/bin/foo [arguments] 

É um toque legal, especialmente para pequenas ferramentas / scripts de propósito especial que podem estar permanentes o lugar.

Essa também parece uma prática comum nas ferramentas GNU, consulte ls por exemplo:

% ls --qq ls: unrecognized option "--qq" Try "ls --help" for more information. % /bin/ls --qq /bin/ls: unrecognized option "--qq" Try "/bin/ls --help" for more information. 

Comentários

  • +1. Eu ia sugerir o mesmo. Estranho que tantas pessoas se concentrem na mudança de comportamento e deixem de mencionar provavelmente o mais óbvio e uso muito mais difundido.

Resposta

Alguém executa o programa digitando: program_name0 arg1 arg2 arg3 ....

Portanto, o shell já deve dividir o token, e o primeiro token já é o nome do programa. A propósito, há os mesmos índices no lado do programa e no shell.

Acho que foi apenas um truque de conveniência (no início) e, como você pode ver nas outras respostas, também foi muito útil, então essa tradição foi continuada e s et como API.

Resposta

Basicamente, argv inclui o nome do programa para que você possa escrever mensagens de erro como prgm: file: No such file or directory, que seria implementado com algo assim:

 fprintf( stderr, "%s: %s: No such file or directory\n", argv[0], argv[1] ); 

Resposta

Outro exemplo de aplicação disso é este programa, que se substitui por … ele mesmo, até que você digite algo que não seja “t y.

#include <unistd.h> #include <stdio.h> #include <stdlib.h> int main (int argc, char** argv) { (void) argc; printf("arg: %s\n", argv[1]); int count = atoi(argv[1]); if ( getchar() == "y" ) { ++count; char buf[20]; sprintf(buf, "%d", count); char* newargv[3]; newargv[0] = argv[0]; newargv[1] = buf; newargv[2] = NULL; execve(argv[0], newargv, NULL); } return count; } 

Obviamente, um exemplo artificial, embora interessante, mas acho que isso pode ter usos reais – por exemplo, um binário de atualização automática, que reescreve seu próprio espaço de memória com uma nova versão de si mesmo que ele baixou ou alterou.

Exemplo:

$ ./res 1 arg: 1 y arg: 2 y arg: 3 y arg: 4 y arg: 5 y arg: 6 y arg: 7 n 7 | $ 

Fonte e mais algumas informações .

Comentários

  • Parabéns por alcançar 1000.

Resposta

O caminho para o programa é argv[0], para que o programa possa recupere os arquivos de configuração etc. de seu diretório de instalação.
Isso seria impossível sem argv[0].

Comentários

  • Essa ‘ não é uma explicação particularmente boa – não ‘ há razão para não podermos ‘ t padronizamos algo como (char *path_to_program, char **argv, int argc) por exemplo
  • Afaik, a maioria dos programas obtém a configuração de um local padrão (~/.<program>, /etc/<program, $XDG_CONFIG_HOME ) e tomar um parâmetro para alterá-lo ou ter uma opção de tempo de compilação que assenta em uma constante para o binário.

Resposta

ccache se comporta dessa maneira para imitar diferentes chamadas para binários do compilador. ccache é um cache de compilação – o objetivo principal é nunca compilar o mesmo código-fonte duas vezes, mas em vez disso retornar o código-objeto do cache, se possível.

Do Página do manual do ccache , “há duas maneiras de usar o ccache. Você pode prefixar seus comandos de compilação com ccache ou pode deixar o ccache se mascarar como o compilador criando um link simbólico (nomeado como o compilador) para o ccache. O primeiro método é mais conveniente se você deseja apenas experimentar o ccache ou deseja usá-lo para alguns projetos específicos. O segundo método é mais útil para quando você deseja usar o ccache para todas as suas compilações. “

O método de links simbólicos envolve a execução destes comandos:

cp ccache /usr/local/bin/ ln -s ccache /usr/local/bin/gcc ln -s ccache /usr/local/bin/g++ ln -s ccache /usr/local/bin/cc ln -s ccache /usr/local/bin/c++ ... etc ... 

… o efeito disso é permitir que o ccache pegue quaisquer comandos que de outra forma teriam ido para os compiladores, permitindo assim que o ccache retorne um arquivo em cache ou passe o comando para o compilador real.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *