Por que argv inclui o nome do programa?


106

Programas típicos do Unix / Linux aceitam as entradas da linha de comandos como um argumento count ( 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 argumento? Existem exemplos de programas usando seu próprio nome (talvez algum tipo de execsituação)?


6
como mv e cp?
Archemar 12/10

9
No Debian shé o link simbólico para dash. Eles se comportam diferente, quando chamado como shou comodash
Motte001

21
@AlexejMagura Se você usa algo como busybox(comum em discos de resgate e outros), praticamente tudo (cp, mv, rm, ls, ...) é um link simbólico para o busybox.
Baard Kopperud

11
Eu estou achando isso muito difícil de ignorar, por isso vou dizer isso: você provavelmente significa programas de "GNU" ( gcc, bash, gunzip, a maior parte do resto do OS ...), como Linux é apenas o kernel.
Wizzwizz4

10
@ wizzwizz4 O que há de errado com "Programas típicos Unix / Linux"? Eu li como "Programas típicos rodando em Unix / Linux". Isso é muito melhor do que a sua restrição a certos programas GNU. Dennis Ritchie certamente não estava usando nenhum programa GNU. BTW o kernel Hurd é um exemplo de um programa GNU que não tem uma função principal ...
rudimeier

Respostas:


122

Para começar, observe que esse argv[0]não é necessariamente o nome do programa. É o que o chamador faz argv[0]da execvechamada do sistema (por exemplo, veja esta pergunta no Stack Overflow ). (Todas as outras variantes de execnão são chamadas de 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]está definido como top, e é isso que psou (o real) topexibiria. Veja esta resposta no U&L SE para obter mais informações.

Colocando tudo isso de lado: Antes do advento de sistemas de arquivos sofisticados /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, os 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 logon no syslog geralmente precedem seu nome às entradas de log; sem isso, o acompanhamento de eventos se tornaria quase inviável.

18
Exemplos de tais programas são bunzip2, bzcate bzip2, para os quais os dois primeiros são links simbólicos para o terceiro.
Ruslan #

5
@Ruslan Curiosamente zcatnão é um link simbólico. Eles parecem evitar as desvantagens dessa técnica usando um script de shell. Mas eles não conseguem imprimir uma --helpsaída completa porque alguém que adicionou opções ao gzip também esqueceu de manter o zcat.
Rudimeier # 12/16

1
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 em geral" na versão atual ). gunzipé uma exceção histórica.

19
O busybox é outro excelente exemplo. Ele pode ser chamado por 308 nomes diferentes para chamar comandos diferentes: busybox.net/downloads/BusyBox.html#commands
Pepijn Schmitz

2
Muitos outros programas também injetam sua argv[0]saída de uso / ajuda em vez de codificar seu nome. Alguns na íntegra, outros apenas o nome da base.
espectros

62

Muito:

  • O Bash é executado no modo POSIX quando argv[0]está sh. É executado como um shell de login quando argv[0]começa com -.
  • Vim comportamento diferente quando executado como vi, view, evim, eview, ex, vimdiff, etc.
  • Busybox, como já mencionado.
  • Em sistemas com Init systemd como, shutdown, reboot, etc, são ligações simbólicas parasystemctl .
  • e assim por diante.

7
Outro é sendmaile mail. Todo MTA unix vem com um link simbólico para esses dois comandos e é projetado para simular o comportamento do original quando chamado como tal, o que significa que qualquer programa unix que precise enviar e-mail sabe exatamente como pode fazê-lo.
Shadur 12/10

4
outro caso comum: teste [: quando você chama o primeiro, ele lida com um erro se o último argumento for ]. (no Debian estável atual, esses comandos são dois programas diferentes, mas as versões anteriores e os MacOs ainda usam o mesmo programa). E tex, latexe assim por diante: o binário é o mesmo, mas olhando como foi chamado, ele escolhe o bom configuração arquivo. initÉ similar.
Giacomo Catenazzi

4
Relacionado, [considera um erro se o último argumento não for ].
Chepner # 13/16

Acho que isso responde à segunda pergunta, mas não à primeira. Duvido muito que um designer de sistema operacional se sentasse e dissesse »Ei, seria legal se eu tivesse o mesmo programa fazendo coisas diferentes apenas com base no nome do executável. Acho que vou incluir o nome em sua matriz de argumentos, então. «
Joey

@Joey Sim, o texto pretende transmitir que (Q: "Há algum ...?" A: "A abundância: ...")
Muru

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 por acaso é o nome do programa.

E existem 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 consigo pensar é o busybox , que atua como várias dezenas de "comandos" diferentes, dependendo de como é chamado .

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

Pode-se ver, por exemplo, a partir da função principalcc disso argce argvjá foram utilizados. O shell copia argumentos para parbufdentro da newargparte do loop, enquanto trata o próprio comando da mesma maneira que os argumentos. (Obviamente, mais tarde, ele executa apenas o primeiro argumento, que é o nome do comando). Parece execve parentes não existiam então.


1
adicione referências que apoiam isso.
Lesmana

Em uma rápida verificação, execleva o nome do comando a ser executado e uma matriz de ponteiros de caracteres com terminação zero (melhor visualizada em minnie.tuhs.org/cgi-bin/utree.pl?file=V1/u0.s , onde execleva referências ao rótulo 2 e ao rótulo 1, e no rótulo 2:aparece etc/init\0, e no rótulo 1:aparece uma referência ao rótulo 2 e um zero final), que é basicamente o execveque hoje menos envp.
Ninjalj 13/10

1
execve execlexistiram "para sempre" (ou seja, desde o início até meados da década de 1970) - execvfoi uma chamada do sistema e execluma função da biblioteca que a chamou.   execvenão existia então porque o ambiente não existia então. Os outros membros da família foram adicionados mais tarde.
G-Man

@ G-Man Você pode me indicar execva fonte v1 que eu vinculei? Apenas curioso.
dirkt 14/10/16

22

Casos de uso:

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

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

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

Isso também é usado no setarchutil-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 carregar módulos a partir de um caminho relativo ao local do programa .

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

Porquê :

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

argv é uma matriz de sequências de argumentos passadas para o novo programa. Por convenção, a primeira dessas seqüências deve conter o nome do arquivo associado ao arquivo que está sendo executado.

  1. É padrão C (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 indica "nome do programa" e não "nome do arquivo".


3
Isso não interrompe se você alcançar o link simbólico de outro link simbólico?
Mehrdad 12/10

3
@ Mehrdad, Sim, essa é a desvantagem e pode ser confusa para o usuário.
Rudimeier 12/10

@rudimeier: Seus itens 'Por que' não são realmente razões, são apenas um "homúnculo", ou seja, apenas implora a questão de por que o padrão exige que este seja o caso.
einpoklum

A pergunta do @einpoklum OP foi: Por que o nome do programa é passado para o executável? Eu respondi: Porque os padrões POSIX e C nos dizem para fazê-lo. Como você acha que isso não é realmente um motivo ? Se os documentos que citei não existirem, provavelmente muitos programas não passarão o nome do programa.
Rudimeier # 15/16

O OP está efetivamente perguntando "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 autores.
user2338816

21

Além de programas que alteram seu comportamento, dependendo de como eles foram chamados, acho argv[0]útil 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. Inclusive inclui 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 estar espalhados por todo o lugar.

Isso parece prática comum também nas ferramentas GNU, veja, lspor 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.

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

5

A pessoa executa a digitação 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, para que haja os mesmos índices no lado do programa e no shell.

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


4

Basicamente, 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 como isto:

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

2

Outro exemplo de aplicação disso é este programa, que se substitui por ... próprio, até você digitar algo que não é 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, se interessante, mas acho que isso pode ter usos reais - por exemplo, um binário auto-atualizável, que reescreve seu próprio espaço de memória com uma nova versão de si mesma que foi baixada ou alterada.

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 .


Parabéns pela atingindo 1000.
G-Man

0

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


2
Isso não é particularmente uma boa explicação - não há nenhuma razão que não poderia ter padronizado em algo como (char *path_to_program, char **argv, int argc)por exemplo
moopet

Afaik, a maioria dos programas de puxar configuração a partir de uma localização 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 coze em uma constante para o binário.
Xiong Chiamiov

0

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

Na página de manual do ccache , "existem duas maneiras de usar o ccache. Você pode prefixar seus comandos de compilação com o ccache ou pode deixar o ccache se disfarçar como compilador criando um link simbólico (chamado de compilador) para o ccache. é mais conveniente se você quiser experimentar o ccache ou desejar usá-lo em 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 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 é permitir ao ccache capturar todos os comandos que seriam enviados aos compiladores, permitindo que o ccache retorne um arquivo em cache ou transmita o comando ao compilador real.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.