Por que o argv inclui o nome do programa?

104

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

Por que o nome do programa foi passado para o executável como um argumento? Há algum exemplo de programas usando seu próprio nome (talvez algum tipo de situação exec )?

    
por Shrikant Giridhar 12.10.2016 / 07:20

10 respostas

119

Para começar, observe que argv[0] não é necessariamente o nome do programa. É o que o autor da chamada coloca em argv[0] da chamada do sistema execve (por exemplo, consulte esta questão 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 (real) top exibiria. Consulte esta resposta em U & L SE para saber mais sobre isso.

Definindo tudo isso de lado: 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 fazem o log através do syslog geralmente prefixam seu nome às entradas de log; sem isso, o acompanhamento de eventos se tornaria quase inviável.
por 12.10.2016 / 08:04
60

Muita:

  • O Bash é executado no modo POSIX quando argv[0] é% código%. Ele é executado como um shell de login quando sh começa com argv[0] .
  • O comportamento do Vim é diferente quando executado como - , vi , view , evim , eview , ex , etc.
  • Busybox, como já mencionado.
  • Em sistemas com systemd como init, vimdiff , shutdown , etc. são links simbólicos para reboot .
  • e assim por diante.
por 12.10.2016 / 07:55
34

Historicamente, argv é apenas uma matriz de ponteiros para as "palavras" da linha de comando, portanto, faz sentido começar com a primeira "palavra", que é o nome do programa.

E há alguns programas que se comportam de maneira diferente, de acordo com o nome usado para chamá-los, para que você possa criar links diferentes para eles e obter "comandos" diferentes. O exemplo mais extremo que posso imaginar é o busybox , que funciona como várias dúzias de" comandos "diferentes, dependendo de como é chamado .

Editar : Referências para a primeira edição do Unix, conforme solicitado

Pode-se ver, por exemplo da função principal de cc that argc e argv já foram utilizados. O shell copia argumentos para o parbuf dentro do newarg parte do loop, enquanto tratava o comando em si da mesma maneira que os argumentos. (É claro que, mais tarde, ele executa apenas o primeiro argumento, que é o nome do comando). Parece que execv e parentes não existiam.

    
por 12.10.2016 / 07:43
22

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 onde esta técnica é usada é o projeto busybox que instala apenas um único binário 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 carregar alguns módulos ou dados em tempo de execução. Ter o caminho do programa permite que você carregue 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 is an array of argument strings passed to the new program. By convention, the first of these strings should contain the filename associated with the file being executed.

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

If the value of argc is greater than zero, the string pointed to by argv[0] represents the program name; argv[0][0] shall be the null character if the program name is not available from the host environment.

Observe que o padrão C diz "nome do programa" e não "nome do arquivo".

    
por 12.10.2016 / 07:46
21

Além de programas que alteram seu comportamento dependendo de como foram chamados, acho argv[0] útil para imprimir o uso de um programa, da seguinte forma:

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 será alterada. Inclui até 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 agradável, especialmente para pequenas ferramentas / scripts para fins especiais que podem viver em todo o lugar.

Isso parece ser uma prática comum nas ferramentas GNU, veja 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.
    
por 12.10.2016 / 10:55
5

Um executa a digitação do programa: program_name0 arg1 arg2 arg3 ... .

Portanto, o shell já deve dividir o token e o primeiro token já é o nome do programa. E BTW então existem os mesmos índices no lado do programa e no shell.

Acho que isso foi apenas um truque de conveniência (bem no início) e, como você vê em outras respostas, também foi muito útil, então essa tradição foi continuada e definida como API.

    
por 12.10.2016 / 17:45
4

Basicamente, o argv inclui o nome do programa para que você possa escrever mensagens de erro como prgm: file: No such file or directory , que seriam implementadas com algo parecido com isto:

    fprintf( stderr, "%s: %s: No such file or directory\n", argv[0], argv[1] );
    
por 12.10.2016 / 18:43
2

Outro exemplo de uma aplicação deste é este programa, que se substitui por ... próprio, até que você digite algo que não seja 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 interessante, mas interessante, mas acho que isso pode ter usos reais - por exemplo, um binário de auto-atualização, que reescreve seu próprio espaço de memória com uma nova versão de si mesmo que 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 .

    
por 14.10.2016 / 00:43
0

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

    
por 16.10.2016 / 17:23
0

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

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

O método symlinks envolve a execução desses 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 prenda todos os comandos que de outra forma teriam ido para os compiladores, permitindo assim que o ccache retornasse um arquivo em cache ou passasse o comando para o compilador real.

    
por 18.10.2016 / 10:46