De ce argv include numele programului?

Programele tipice Unix / Linux acceptă intrările din linia de comandă ca număr de argumente (int argc) și vector de argument (char *argv[]). Primul element al argv este numele programului – urmat de argumentele reale.

De ce numele programului este transmis executabilului ca argument? Există exemple de programe care folosesc propriul nume (poate un fel de exec situație)?

Comentarii

  • cum ar fi mv și cp?
  • Pe Debian sh este legătură simbolică către dash. Se comportă diferit, atunci când sunt numiți ca sh sau ca dash
  • @AlexejMagura Dacă utilizați ceva de genul busybox (obișnuit pe discurile de salvare și altele), atunci aproape totul (cp, mv, rm, ls, …) este o legătură simbolică către busybox.
  • ‘ consider că acest cu adevărat este greu de ignorat, așa că ‘ spune-l: probabil că vrei să spui ” programe GNU ” (gcc, bash, gunzip, majoritatea restului sistemului de operare …), deoarece Linux este doar nucleul.
  • @ wizzwizz4 Ce ‘ nu este în regulă cu ” programe tipice Unix / Linux „? L-am citit ca ” Programe tipice care rulează pe Unix / Linux „. Aceasta ‘ este mult mai bună decât restricția dvs. la anumite programe GNU. Dennis Ritchie nu folosea cu siguranță niciun program GNU. BTW, nucleul Hurd este un exemplu de program GNU care nu are o funcție principală …

Răspuns

Pentru început, rețineți că argv[0] nu este neapărat numele programului. Este ceea ce apelantul pune în argv[0] al apelului de sistem execve (de exemplu, consultați această întrebare pe Stack Overflow ). (Toate celelalte variante ale exec nu sunt apeluri de sistem, ci interfețe către execve.)

Să presupunem, de exemplu, următoarele (folosind execl):

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

/var/tmp/mybackdoor este ceea ce este executat, dar argv[0] este setat la top, iar asta este ps sau ( real) top s-ar afișa. Consultați acest răspuns pe U & L SE pentru mai multe despre acest lucru.

Setarea tuturor acest lucru deoparte: Înainte de apariția unor sisteme de fișiere fanteziste precum /proc, argv[0] a fost singura modalitate prin care un proces a aflat despre propriul său nume. La ce ar fi bine?

  • Mai multe programe își personalizează comportamentul în funcție de numele cu care au fost numiți (de obicei prin legături simbolice sau dure, de exemplu Utilitățile BusyBox ; mai multe exemple sunt oferite în alte răspunsuri la această întrebare).
  • Mai mult, serviciile, demonii și alte programe care se conectează prin syslog își aduc adesea numele în intrări de jurnal; fără aceasta, urmărirea evenimentelor ar deveni aproape imposibilă.

Comentarii

  • Exemple de astfel de programe sunt bunzip2, bzcat și bzip2, pentru care primele două sunt linkuri simbolice către a treia.
  • @Ruslan În mod interesant, zcat nu este o legătură simbolică. Se pare că evită dezavantajele acestei tehnici folosind un script shell. Dar nu reușesc să tipărească un ieșire, deoarece cineva care a adăugat opțiuni la gzip a uitat de main Tain și zcat.
  • Atât timp cât îmi amintesc, standardele de codare GNU au descurajat utilizarea argv [0] pentru a modifica comportamentul programului (secțiunea ” Standarde pentru interfețe În general ” în versiunea curentă ). gunzip este o excepție istorică.
  • busybox este un alt exemplu excelent. Poate fi apelat cu 308 nume diferite pentru a invoca comenzi diferite: busybox.net/downloads/BusyBox.html#commands
  • Multe, multe mai multe programe își injectează, de asemenea, argv[0] în ieșirea lor de utilizare / ajutor în loc să-și codifice numele. Unele în întregime, altele doar numele de bază.

Răspuns

Abundență:

  • Bash rulează în modul POSIX când argv[0] este sh. Se execută ca un shell de conectare când argv[0] începe cu -.
  • Vim se comportă diferit atunci când rulează ca vi, view, evim, eview, ex, vimdiff etc.
  • Busybox, așa cum s-a menționat deja.
  • În sistemele cu systemd ca init, shutdown, reboot etc. sunt link-uri simbolice către systemctl .
  • și așa mai departe.

Comentarii

  • Un altul este sendmail și mail. Fiecare MTA unix vine cu un link simbolic pentru aceste două comenzi și este conceput pentru a imita comportamentul original ‘ atunci când este apelat ca atare, ceea ce înseamnă că orice program Unix care trebuie să trimită e-mail știe exact cum pot face acest lucru.
  • un alt caz obișnuit: test și [: când îl apelați pe primul , gestionează o eroare dacă ultimul argument este ]. (pe Debian stabil actual aceste comenzi sunt două programe diferite, dar versiunile anterioare și MacO-urile folosesc în continuare același program). Și tex, latex și așa mai departe: binarul este același, dar uitându-se la modul în care a fost numit, a ales corect fișier de configurare . init este similar.
  • În legătură, [ consideră că este o eroare dacă ultimul argument nu ].
  • Cred că acesta răspunde la a doua întrebare, dar nu la prima. Mă îndoiesc că un designer de sisteme de operare s-a așezat și a spus » Hei, ar fi grozav dacă aș avea același program făcând lucruri diferite doar pe baza numelui său executabil. Cred că ‘ voi include numele în matricea sa de argumente, apoi. «
  • @Joey Da, formularea este menită să transmită că (Q: ” Există …? ” A: ” Multime: … „)

Răspuns

Din punct de vedere istoric, argv este doar o serie de indicatori către „cuvintele” din linia de comandă, deci este logic să începeți cu primul „cuvânt”, care se întâmplă să fie numele programului.

Și există destul de multe programe care se comportă diferit în funcție de numele care este folosit pentru a le apela, astfel încât să puteți crea linkuri diferite către ele și să obțineți „comenzi” diferite. cel mai extrem exemplu la care mă gândesc este busybox , care acționează ca câteva zeci de „comenzi” diferite, în funcție de modul în care se numește .

Editați

: Referințe pentru ediția 1 a Unix, așa cum se solicită

Se pot vedea de ex. din funcția principal a ccargc și argv au fost deja utilizate. shell copiază argumente în parbuf din partea newarg a bucla, în timp ce tratează comanda în sine la fel ca argumentele. (Desigur, mai târziu execută doar primul argument, care este numele comenzii). Se pare că execv și rudele nu existau atunci.

Comentarii

  • vă rugăm să adăugați referințe care faceți o copie de rezervă.
  • Dintr-o descriere rapidă, exec preia numele comenzii de executat și o matrice terminată cu zero de indicatori char (cel mai bine văzut la minnie.tuhs.org/cgi-bin/utree.pl?file=V1/u0.s , unde exec ia referințe la eticheta 2 și eticheta 1, iar la eticheta 2: apare etc/init\0 și la eticheta 1: apare o referință la eticheta 2 și un zero final), ceea ce face practic execve astăzi minus envp.
  • execv și execl au existat ” pentru totdeauna ” (adică, de la începutul până la mijlocul anilor 1970) – execv a fost un apel de sistem și a fost o funcție de bibliotecă care a numit-o. execve nu exista ‘ atunci deoarece mediul nu exista ‘ atunci nu exista. Ceilalți membri ai familiei au fost adăugați mai târziu.
  • @ G-Man Puteți să mă indicați spre execv în sursa v1 pe care am conectat-o? Doar curios.

Răspuns

Cazuri de utilizare:

Puteți utiliza numele programului pentru a schimba comportamentul programului .

De exemplu, puteți crea câteva legături simbolice către binele propriu-zis.

Un exemplu celebru în care se folosește această tehnică este proiectul busybox care instalează doar un singur binar și multe legături simbolice către acesta. (ls, cp, mv etc.). O fac pentru a economisi spațiu de stocare deoarece țintele lor sunt dispozitive încorporate mici.

folosit în setarch din 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 

Aici utilizează această tehnică practic pentru a evita multe fișiere sursă duplicate sau doar pentru a menține sursele mai lizibile.

Un alt caz de utilizare ar fi un program care are nevoie de pentru a încărca unele module sau date în timpul rulării. Având calea programului, puteți face să încărcați module dintr-o cale relativă la locația programului .

Mai mult, multe programe imprimă mesaje de eroare, inclusiv numele programului .

De ce :

  1. Deoarece este convenția POSIX ( man 3p execve):

argv este o matrice de șiruri de argumente transmise noului program. Prin convenție, primul dintre aceste șiruri ar trebui să conțină numele de fișier asociat cu fișierul executat.

  1. Este „C standard (cel puțin C99 și C11):

Dacă valoarea argc este mai mare decât zero, șirul indicat de argv [0 ] reprezintă numele programului; argv [0] [0] va fi caracterul nul dacă numele programului nu este disponibil din mediul gazdă.

Rețineți că standardul C spune „programul” nume „nu” nume de fișier ”.

Comentarii

  • Nu ‘ nu face această pauză dacă ajungeți la link simbolic dintr-un alt link simbolic?
  • @Mehrdad, Da, ‘ este dezavantajul și poate fi confuz pentru utilizator.
  • @rudimeier: ‘ De ce ‘ elementele dvs. nu sunt într-adevăr motive, ele ‘ sunt doar ” homunculus „, adică pune doar întrebarea de ce standardul impune acest lucru.
  • @ Întrebarea einpoklum OP ‘ a fost: De ce numele programului este transmis executabilului? Am răspuns: Pentru că standardele POSIX și C ne spun să facem acest lucru. Cum credeți că ‘ nu este într-adevăr un motiv ? Dacă documentele pe care ‘ le-am citat nu ar exista, probabil că multe programe nu vor trece numele programului.
  • OP solicită efectiv ” DE CE spun standardele POSIX și C să facă acest lucru? ” Acordat că formularea a fost la un nivel abstractizat, dar pare clar. În mod realist, singura modalitate de a ști este să întrebați inițiatorii.

Răspuns

Pe lângă programele care își modifică comportament în funcție de modul în care au fost numiți, mi se pare argv[0] util în imprimarea utilizării unui program, așa:

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

Acest lucru face ca mesajul de utilizare să utilizeze întotdeauna numele prin care a fost apelat. Dacă programul este redenumit, mesajul său de utilizare se modifică odată cu acesta. Include chiar numele căii cu care a fost numit:

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

Este o atingere plăcută, în special pentru instrumentele / scripturile cu scop special care ar putea trăi peste tot locul.

Aceasta pare o practică obișnuită și în instrumentele GNU, vezi ls de exemplu:

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

Comentarii

  • +1. Aveam să sugerez același lucru. Ciudat că atât de mulți oameni se concentrează pe schimbarea comportamentului și nu menționează probabil cele mai evidente și utilizare mult mai răspândită.

Răspuns

Unul execută programul tastând: program_name0 arg1 arg2 arg3 ....

Deci, shell-ul ar trebui să împartă deja token-ul, iar primul token este deja numele programului. Și BTW, deci există aceiași indici pe partea de program și pe shell.

Cred că acesta a fost doar un truc de comoditate (chiar de la început) și, așa cum vedeți în alte răspunsuri, a fost, de asemenea, foarte la îndemână, așa că această tradiție a fost continuată și s et ca API.

Răspuns

Practic, argv include numele programului, astfel încât să puteți scrie mesaje de eroare precum prgm: file: No such file or directory, care ar fi implementat cu ceva de genul acesta:

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

Răspuns

Un alt exemplu de aplicație a acestui program este acest program, care se înlocuiește cu … însuși, până când tastați ceva care nu este „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; } 

Evident, un fel de exemplu inventat, dar interesant, dar cred că acest lucru poate avea utilizări reale – de exemplu, un binar auto-actualizat, care rescrie propriul spațiu de memorie cu o nouă versiune a sa pe care a descărcat-o sau a schimbat-o.

Exemplu:

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

Sursă și câteva informații suplimentare .

Comentarii

  • Felicitări pentru că ați ajuns la 1000.

Răspuns

Calea către program este argv[0], astfel încât programul să poată preluați fișierele de configurare etc. din directorul său de instalare.
Acest lucru ar fi imposibil fără argv[0].

Comentarii

  • Că ‘ nu este o explicație deosebit de bună – nu există ‘ motiv pentru care nu putem ‘ nu s-au standardizat pe ceva de genul (char *path_to_program, char **argv, int argc) de exemplu
  • Afaik, majoritatea programelor extrag configurația dintr-o locație standard (~/.<program>, /etc/<program, $XDG_CONFIG_HOME ) și fie ia un parametru pentru a-l modifica, fie are o opțiune în timp de compilare care se coace într-o constantă la binar.

Răspuns

ccache se comportă astfel pentru a imita apeluri diferite către binele compilatorului. ccache este o memorie cache de compilare – întregul punct nu este niciodată să compilezi același cod sursă de două ori, ci în schimb să returnezi codul obiect din cache, dacă este posibil.

Din ccache man page , „există două moduri de utilizare a ccache. Puteți prefixa comenzile de compilare cu ccache sau puteți lăsa ccache să se prefacă în compilator prin crearea unei legături simbolice (denumite compilator) către ccache. prima metodă este cea mai convenabilă dacă doriți doar să încercați ccache sau doriți să o utilizați pentru anumite proiecte specifice. A doua metodă este cea mai utilă atunci când doriți să utilizați ccache pentru toate compilațiile dvs. „.

metoda legăturilor simbolice implică executarea acestor comenzi:

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

… al cărui efect este de a permite ccache să blocheze orice comenzi care altfel ar fi fost trimise la compilatoare, permițând astfel ccache să returneze un fișier memorat în cache sau să transmită comanda către compilatorul propriu-zis.

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *