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
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
șibzip2
, 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]
estesh
. Se execută ca un shell de conectare cândargv[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ătresystemctl
. - și așa mai departe.
Comentarii
- Un altul este
sendmail
șimail
. 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). Șitex
,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 cc
că argc
ș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 , undeexec
ia referințe la eticheta 2 și eticheta 1, iar la eticheta2:
apareetc/init\0
și la eticheta1:
apare o referință la eticheta 2 și un zero final), ceea ce face practicexecve
astăzi minusenvp
. -
execv
șiexecl
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 :
- 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.
- 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.
sh
este legătură simbolică cătredash
. Se comportă diferit, atunci când sunt numiți cash
sau cadash
busybox
(obișnuit pe discurile de salvare și altele), atunci aproape totul (cp, mv, rm, ls, …) este o legătură simbolică către busybox.gcc
,bash
,gunzip
, majoritatea restului sistemului de operare …), deoarece Linux este doar nucleul.