Perché argv include il nome del programma?

I tipici programmi Unix / Linux accettano gli input della riga di comando come un conteggio di argomenti (int argc) e un vettore di argomenti (char *argv[]). Il primo elemento di argv è il nome del programma, seguito dagli argomenti effettivi.

Perché il nome del programma viene passato alleseguibile come argomento? Esistono esempi di programmi che utilizzano il proprio nome (forse una sorta di exec situazione)?

Commenti

  • come mv e cp?
  • Su Debian sh è un collegamento simbolico a dash. Si comportano in modo diverso, se chiamati come sh o come dash
  • @AlexejMagura Se usi qualcosa come busybox (comune sui dischi di ripristino e simili), quindi praticamente tutto (cp, mv, rm, ls, …) è un collegamento simbolico a busybox.
  • ‘ trovo che sia davvero difficile da ignorare, quindi ‘ ll dillo: probabilmente intendi ” GNU ” programmi (gcc, bash, gunzip, la maggior parte del resto del sistema operativo …), poiché Linux è solo il kernel.
  • @ wizzwizz4 Che cosa cè di sbagliato nei ‘ ” tipici programmi Unix / Linux “? Lho letto come ” Programmi tipici in esecuzione su Unix / Linux “. Questo ‘ è molto meglio della tua restrizione a certi programmi GNU. Dennis Ritchie non stava certamente usando alcun programma GNU. A proposito, il kernel Hurd è un esempio di un programma GNU che non ha una funzione principale …

Risposta

Per cominciare, nota che argv[0] non è necessariamente il nome del programma. È ciò che il chiamante inserisce in argv[0] della execve chiamata di sistema (ad esempio, vedere questa domanda su Stack Overflow ). (Tutte le altre varianti di exec non sono chiamate di sistema ma interfacce per execve.)

Supponi, ad esempio, il seguente (utilizzando execl):

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

/var/tmp/mybackdoor è cosa viene eseguito ma argv[0] è impostato su top, e questo è ciò che ps o ( il vero) top verrà visualizzato. Vedi questa risposta su U & L SE per ulteriori informazioni al riguardo.

Impostazione di tutto a parte questo: prima dellavvento di fantasiosi filesystem come /proc, argv[0] era lunico modo per un processo di conoscere il proprio nome. A cosa servirebbe?

  • Diversi programmi personalizzano il loro comportamento a seconda del nome con cui sono stati chiamati (di solito tramite link simbolici o fisici, ad esempio utilità di BusyBox ; molti altri esempi sono forniti in altre risposte a questa domanda).
  • Inoltre, i servizi, i daemon e altri programmi che accedono tramite syslog spesso antepongono il loro nome al voci di log; senza questo, il monitoraggio degli eventi diventerebbe quasi impossibile.

Commenti

  • Esempi di tali programmi sono bunzip2, bzcat e bzip2, per i quali i primi due sono collegamenti simbolici al terzo.
  • @Ruslan È interessante notare che zcat non è un collegamento simbolico. Sembra che evitino gli svantaggi di questa tecnica utilizzando uno script di shell. Ma non riescono a stampare un perché qualcuno che ha aggiunto opzioni a gzip si è dimenticato di main tain anche zcat.
  • Per tutto il tempo che posso ricordare, gli standard di codifica GNU hanno scoraggiato luso di argv [0] per cambiare il comportamento del programma ( sezione ” Standard per le interfacce in genere ” nella versione corrente ). gunzip è uneccezione storica.
  • busybox è un altro eccellente esempio. Può essere chiamato con 308 nomi diversi per richiamare comandi diversi: busybox.net/downloads/BusyBox.html#commands
  • Many, many più programmi inseriscono anche il loro argv[0] nel loro output di utilizzo / aiuto invece di codificare il loro nome. Alcuni per intero, altri solo il nome di base.

Risposta

Un sacco:

  • Bash viene eseguito in modalità POSIX quando argv[0] è sh. Funziona come una shell di accesso quando argv[0] inizia con -.
  • Vim si comporta in modo diverso quando viene eseguito come vi, view, evim, eview, ex, vimdiff e così via
  • Busybox, come già accennato.
  • Nei sistemi con systemd come init, shutdown, reboot, ecc. Sono link simbolici a systemctl .
  • e così via.

Commenti

  • Un altro è sendmail e mail. Ogni singolo MTA unix viene fornito con un collegamento simbolico per questi due comandi ed è progettato per emulare il comportamento originale di ‘ quando viene chiamato come tale, il che significa che qualsiasi programma unix che deve inviare posta lo sa esattamente come possono farlo.
  • un altro caso comune: test e [: quando chiami il primo , gestisce un errore se lultimo argomento è ]. (nellattuale Debian stabile questi comandi sono due programmi differenti, ma le versioni precedenti e MacO usano ancora lo stesso programma). E tex, latex e così via: il binario è lo stesso, ma guardando come è stato chiamato, sceglie quello corretto file di configurazione . init è simile.
  • Correlato, [ lo considera un errore se lultimo argomento non ].
  • Immagino che questo risponda alla seconda domanda, ma non alla prima. Dubito molto che qualche progettista di sistemi operativi si sia seduto e abbia detto » Ehi, sarebbe bello se lo stesso programma facesse cose diverse solo in base al nome del suo eseguibile. Immagino di ‘ includerò il nome nel suo array di argomenti, quindi. «
  • @Joey Sì, il il testo ha lo scopo di trasmettere che (Q: ” Ce ne sono …? ” A: ” Abbondanza: … “)

Risposta

Storicamente, argv è solo un array di puntatori alle “parole” della riga di comando, quindi ha senso iniziare con la prima “parola”, che sembra essere la nome del programma.

E ci sono parecchi programmi che si comportano in modo diverso a seconda del nome utilizzato per chiamarli, quindi puoi semplicemente creare collegamenti diversi e ottenere diversi “comandi”. lesempio più estremo a cui riesco a pensare è busybox , che agisce come diverse dozzine di “comandi” diversi a seconda di come si chiama .

Modifica

: riferimenti per la prima edizione di Unix, come richiesto

Si può vedere ad es. dalla funzione main di cc che argc e argv erano già stati utilizzati. La shell copia gli argomenti nella parbuf allinterno della newarg parte di il ciclo, trattando il comando stesso allo stesso modo degli argomenti. (Ovviamente, in seguito esegue solo il primo argomento, che è il nome del comando). Sembra che execv e i parenti non esistessero allora.

Commenti

  • aggiungi riferimenti che eseguire il backup.
  • Da una rapida occhiata, exec prende il nome del comando da eseguire e un array di puntatori char con terminazione zero (meglio visibile in minnie.tuhs.org/cgi-bin/utree.pl?file=V1/u0.s , dove exec accetta i riferimenti alletichetta 2 e alletichetta 1 e alletichetta 2: viene visualizzato etc/init\0 e alletichetta 1: appare un riferimento alletichetta 2 e uno zero terminale), che è fondamentalmente ciò che execve fa oggi meno envp.
  • execv e execl esistono ” per sempre ” (cioè dallinizio alla metà degli anni 70) – execv era una chiamata di sistema e era una funzione di libreria che lo chiamava. execve ‘ non esisteva allora perché lambiente ‘ non esisteva allora. Gli altri membri della famiglia sono stati aggiunti in seguito.
  • @ G-Man Puoi indicarmi execv nella fonte v1 che ho collegato? Semplicemente curioso.

Risposta

Casi duso:

Puoi utilizzare il nome del programma per modificare il comportamento del programma .

Per esempio potresti creare alcuni link simbolici al binario vero e proprio.

Un famoso esempio in cui viene usata questa tecnica è il progetto busybox che installa un solo binario e molti link simbolici ad esso. (ls, cp, mv, ecc.). Lo stanno facendo per risparmiare spazio di archiviazione perché i loro obiettivi sono piccoli dispositivi incorporati.

Anche questo è utilizzato in setarch da 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 

Qui stanno usando fondamentalmente questa tecnica per evitare molti file sorgente duplicati o semplicemente per mantenere i sorgenti più leggibili.

Un altro caso duso sarebbe un programma che necessita per caricare alcuni moduli o dati in fase di esecuzione. Il percorso del programma consente a di caricare i moduli da un percorso relativo alla posizione del programma .

Inoltre molti programmi stampano messaggi di errore incluso il nome del programma .

Perché :

  1. Perché è la convenzione POSIX ( man 3p execve):

argv è un array di stringhe di argomenti passati al nuovo programma. Per convenzione, la prima di queste stringhe deve contenere il nome del file associato al file in esecuzione.

  1. It “s C standard (almeno C99 e C11):

Se il valore di argc è maggiore di zero, la stringa puntata da argv [0 ] rappresenta il nome del programma; argv [0] [0] deve essere il carattere nullo se il nome del programma non è disponibile dallambiente host.

Nota che lo standard C dice “programma name “not” filename “.

Comments

  • ‘ t questa interruzione se raggiungi il collegamento simbolico da un altro collegamento simbolico?
  • @Mehrdad, Sì, ‘ è il rovescio della medaglia e può creare confusione per lutente.
  • @rudimeier: I tuoi ‘ Perché ‘ articoli non sono un vero motivo, sono ‘ solo un ” homunculus “, cioè solleva solo la questione del perché lo standard richiede che sia così.
  • @ La domanda di einpoklum OP ‘ era: Perché il nome del programma viene passato alleseguibile? Ho risposto: perché lo standard POSIX e C ci dice di farlo. Come pensi che ‘ non sia davvero un motivo ? Se i documenti che ho ‘ citati non esistessero, probabilmente molti programmi non passerebbero il nome del programma.
  • LOP sta effettivamente chiedendo ” PERCHÉ gli standard POSIX e C dicono di farlo? ” Certo, la formulazione era a un livello astratto, ma sembra chiaro. Realisticamente, lunico modo per saperlo è chiedere agli ideatori.

Risposta

Oltre a programmi che alterano il loro comportamento a seconda di come sono stati chiamati, trovo argv[0] utile per stampare lutilizzo di un programma, in questo modo:

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

Questo fa sì che il messaggio di utilizzo utilizzi sempre il nome attraverso il quale è stato chiamato. Se il programma viene rinominato, il suo messaggio di utilizzo cambia con esso. Include anche il nome del percorso con cui è stato chiamato:

# 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] 

È “un bel tocco, specialmente per piccoli strumenti / script speciali che potrebbero vivere ovunque il posto.

Questa sembra pratica comune anche negli strumenti GNU, vedere ls ad esempio:

% 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. 

Commenti

  • +1. Stavo per suggerire la stessa cosa. Strano che così tante persone si concentrino sul cambiamento del comportamento e non menzionino probabilmente il più ovvio e utilizzo molto più diffuso.

Risposta

Si esegue il programma digitando: program_name0 arg1 arg2 arg3 ....

Quindi la shell dovrebbe già dividere il token e il primo token è già il nome del programma. E a proposito, ci sono gli stessi indici sul lato del programma e sulla shell.

Penso che questo fosse solo un trucco di convenienza (fin dallinizio) e, come vedrai in altre risposte, era anche molto utile, quindi questa tradizione è stata continuata es et come API.

Risposta

Fondamentalmente, argv include il nome del programma in modo da poter scrivere messaggi di errore come prgm: file: No such file or directory, che verrebbe implementato con qualcosa di simile:

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

Risposta

Un altro esempio di unapplicazione di questo programma è questo programma, che si sostituisce con … stesso, finché non digiti qualcosa che non è “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; } 

Ovviamente, un po un esempio artificioso se interessante, ma penso che questo possa avere usi reali, ad esempio, un binario che si aggiorna automaticamente, che riscrive il proprio spazio di memoria con una nuova versione di se stesso che ha scaricato o modificato.

Esempio:

$ ./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 altre informazioni .

Commenti

  • Congratulazioni per aver raggiunto il 1000.

Risposta

Il percorso del programma è argv[0], in modo che il programma possa recuperare i file di configurazione ecc. dalla sua directory di installazione.
Questo sarebbe impossibile senza argv[0].

Commenti

  • Questa ‘ non è una spiegazione particolarmente valida: ‘ non è motivo per cui ‘ non ho standardizzato qualcosa come (char *path_to_program, char **argv, int argc) per esempio
  • Afaik, la maggior parte dei programmi estrae la configurazione da una posizione standard (~/.<program>, /etc/<program, $XDG_CONFIG_HOME ) e prendere un parametro per cambiarlo o avere unopzione in fase di compilazione che converte una costante nel binario.

Answer

ccache si comporta in questo modo per imitare diverse chiamate ai binari del compilatore. ccache è una cache di compilazione: il punto centrale non è mai compilare lo stesso codice sorgente due volte, ma restituire invece il codice oggetto dalla cache, se possibile.

Dal ccache man page , “ci sono due modi per usare ccache. Puoi aggiungere ccache come prefisso ai comandi di compilazione oppure puoi lasciare che ccache si mascherasse da compilatore creando un collegamento simbolico (chiamato compilatore) a ccache. il primo metodo è più conveniente se vuoi solo provare ccache o se desideri usarlo per alcuni progetti specifici. Il secondo metodo è più utile quando desideri usare ccache per tutte le tue compilation. “

Il Il metodo dei collegamenti simbolici implica lesecuzione di questi comandi:

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 ... 

… il cui effetto è quello di consentire a ccache di catturare qualsiasi comando che altrimenti sarebbe andato ai compilatori, consentendo così a ccache di restituire un file memorizzato nella cache o di passare il comando al compilatore effettivo.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *