Typiska Unix / Linux-program accepterar kommandoradsingångarna som ett argumentantal (int argc
) och en argumentvektor (char *argv[]
). Det första elementet i argv
är programnamnet – följt av de faktiska argumenten.
Varför skickas programnamnet till den körbara filen som ett argument? Finns det några exempel på program som använder sitt eget namn (kanske någon form av exec
situation)?
Kommentarer
Svar
Till att börja med, notera att argv[0]
inte nödvändigtvis är programnamnet. Det är vad den som ringer sätter i argv[0]
i execve
systemanropet (t.ex. se den här frågan om Stack Overflow ). (Alla andra varianter av exec
är inte systemanrop utan gränssnitt till execve
.)
Antag till exempel följande (med execl
):
execl("/var/tmp/mybackdoor", "top", NULL);
/var/tmp/mybackdoor
är vad som körs men argv[0]
är inställt på top
, och detta är vad ps
eller ( den verkliga) top
skulle visas. Se detta svar på U & L SE för mer om detta.
Ställa in alla detta åt sidan: Före tillkomsten av snygga filsystem som /proc
var argv[0]
det enda sättet för en process att lära sig om sitt eget namn. Vad skulle det vara bra för?
- Flera program anpassar sitt beteende beroende på namnet de kallades på (vanligtvis med symboliska eller hårda länkar, till exempel BusyBox verktyg ; flera andra exempel ges i andra svar på denna fråga).
- Dessutom förtjänar tjänster, demoner och andra program som loggar genom syslog ofta sitt namn till loggposter; utan detta skulle händelsespårning bli nästan omöjligt.
Kommentarer
- Exempel på sådana program är
bunzip2
,bzcat
ochbzip2
, för vilka de två första är symlänkar till den tredje. - @Ruslan Intressant
zcat
är inte en symlänk. De verkar undvika nackdelarna med denna teknik med ett skalskript istället. Men de kan inte skriva ut en fullständig--help
-output eftersom någon som lade till alternativ till gzip glömde att main tain zcat också. - Så länge jag kan komma ihåg har GNU-kodningsstandarderna avskräckt från att använda argv [0] för att ändra programbeteende ( avsnitt ” Standarder för gränssnitt Generellt ” i den aktuella versionen ).
gunzip
är ett historiskt undantag. - upptagen låda är ett annat utmärkt exempel. Det kan kallas med 308 olika namn för att anropa olika kommandon: busybox.net/downloads/BusyBox.html#commands
- Många, många fler program injicerar också deras
argv[0]
i deras användnings- / hjälputdata istället för att hårdkoda namnet. Vissa i sin helhet, andra bara basnamnet.
Svar
Gott:
- Bash körs i POSIX-läge när
argv[0]
ärsh
. Det körs som ett inloggningsskal närargv[0]
börjar med-
. - Vim beter sig annorlunda när den körs som
vi
,view
,evim
,eview
,ex
,vimdiff
osv. - Busybox, som redan nämnts.
- I system med systemd som init är
shutdown
,reboot
etc. symlänkar tillsystemctl
. - och så vidare.
Kommentarer
- En annan är
sendmail
ochmail
. Varje unix MTA kommer med en symlink för dessa två kommandon och är utformad för att emulera det ursprungliga ’ beteendet när det kallas som sådant, vilket betyder att alla unix-program som behöver skicka e-post vet exakt hur de kan göra det. - ett annat vanligt fall:
test
och[
: när du ringer till den förra , det hanterar ett fel om det sista argumentet är]
. (på Debian-stabil är dessa kommandon två olika program, men tidigare versioner och MacO använder fortfarande samma program). Ochtex
,latex
och så vidare: binären är densamma, men ser hur den hette, väljer den rätt konfigurationsfil .init
liknar. - Relaterat,
[
anser att det är ett fel om det sista argumentet är inte]
. - Jag antar att det besvarar den andra frågan, men inte den första. Jag tvivlar mycket på att någon OS-designer satte sig ner och sa » Hej, det skulle vara kul om jag hade samma program som gjorde olika saker bara baserat på dess körbara namn. Jag antar att jag ’ kommer att inkludera namnet i dess argumentmatris, då. «
- @Joey Ja, formuleringen är avsedd att förmedla att (Q: ” Finns det någon …? ” A: ” Gott: … ”)
Svar
Historiskt är argv
bara en rad pekare till ”ord” i kommandoraden, så det är vettigt att börja med det första ”ordet”, som råkar vara programmets namn.
Och det finns en hel del program som beter sig olika enligt vilket namn som används för att kalla dem, så du kan bara skapa olika länkar till dem och få olika ”kommandon”. mest extrema exempel jag kan tänka mig är upptagen låda , som fungerar som flera dussin olika ”kommandon” beroende på hur det heter .
Redigera
: Referenser för Unix 1: a upplagan, enligt begäran
Man kan se t.ex. från huvud -funktionen för cc
att argc
och argv
användes redan. skalet kopierar argument till parbuf
inuti newarg
delen av slingan, samtidigt som kommandot behandlas på samma sätt som argumenten. (Självklart kör det senare bara det första argumentet, vilket är kommandot). Det ser ut som att execv
och släktingar inte fanns då.
Kommentarer
- lägg till referenser som säkerhetskopiera detta.
- Från ett snabbt skumning tar
exec
namnet på kommandot som ska köras och en nollavslutad uppsättning char-pekare (ses bäst vid minnie.tuhs.org/cgi-bin/utree.pl?file=V1/u0.s , därexec
tar referenser till etikett 2 och etikett 1 och vid etikett2:
visasetc/init\0
och vid etikett1:
visas en hänvisning till etikett 2 och en avslutande noll), vilket i princip är vadexecve
gör idag minusenvp
. -
execv
ochexecl
har funnits ” för alltid ” (dvs. sedan början till mitten av 1970-talet) –execv
var ett systemanrop och var en biblioteksfunktion som kallade den.execve
existerade inte ’ eftersom miljön inte fanns ’ t då. De andra familjemedlemmarna lades till senare. - @ G-Man Kan du rikta mig till
execv
i v1-källan som jag länkade? Bara nyfiken.
Svar
Användningsfall:
Du kan använda programnamnet för att ändra programbeteendet .
Du kan till exempel skapa några symlänkar till den faktiska binära.
Ett känt exempel där denna teknik används är upptagenboxprojektet som bara installerar en enda binär och många symlänkar till den. (ls, cp, mv, etc). De gör det för att spara lagringsutrymme eftersom deras mål är små inbäddade enheter.
Detta är också används i setarch
från 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
Här använder de denna teknik i princip för att undvika många dubbla källfiler eller bara för att hålla källorna mer läsbara.
Ett annat användningsfall skulle vara ett program som behöver för att ladda vissa moduler eller data vid körning. Med programvägen kan du ladda moduler från en sökväg i förhållande till programplatsen .
Dessutom skriver många program felmeddelanden inklusive programnamnet .
Varför :
- Eftersom det är POSIX-konvention (
man 3p execve
):
argv är en rad argumentsträngar som skickas till det nya programmet. Enligt konvention bör den första av dessa strängar innehålla filnamnet som är associerat med filen som körs.
- Det är C standard (minst C99 och C11):
Om värdet på argc är större än noll, strängen pekad på av argv [0 ] representerar programnamnet; argv [0] [0] ska vara noll tecken om programnamnet inte är tillgängligt från värdmiljön.
Observera att C-standarden säger ”program namn ”inte” filnamn ”.
Kommentarer
- Inte ’ t denna paus om du når symlink från en annan symlink?
- @Mehrdad, Ja att ’ är nackdelen och kan vara förvirrande för användaren.
- @rudimeier: Dina ’ Varför ’ är inte riktigt skäl, de ’ är bara en ” homunculus ”, det vill säga det frågar bara varför kräver standarden att detta ska vara fallet.
- @ einpoklum OP ’: s fråga var: Varför skickas programnamnet till den körbara? Jag svarade: Eftersom POSIX och C-standarden säger att vi ska göra det. Hur tycker du att ’ inte är verkligen en anledning ? Om de dokument som jag ’ har citerat inte skulle existera skulle många program förmodligen inte klara programnamnet.
- OP: n frågar faktiskt ” VARFÖR säger POSIX- och C-standarderna att göra detta? ” Beviljas att formuleringen var på en abstraherad nivå, men det verkar tydligt. Realistiskt är det enda sättet att veta att fråga upphovsmännen.
Svar
Förutom program som ändrar deras beteende beroende på hur de hette, jag tycker att argv[0]
är användbart för att skriva ut användningen av ett program, som så:
printf("Usage: %s [arguments]\n", argv[0]);
Detta gör att användningsmeddelandet alltid använder det namn som det kallades för. Om programmet byts namn ändras dess användningsmeddelande med det. Det innehåller till och med sökvägen som den hette med:
# 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]
Det är en fin touch, speciellt för små specialverktyg / skript som kan leva överallt platsen.
Detta verkar också vara vanligt i GNU-verktyg, se ls
till exempel:
% 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.
Kommentarer
- +1. Jag tänkte föreslå detsamma. Konstigt att så många människor fokuserar på att ändra beteende och inte nämner förmodligen det mest uppenbara och mycket mer utbredd användning.
Svar
Man kör programtypen: program_name0 arg1 arg2 arg3 ...
.
Så skalet ska redan dela token, och den första token är redan programnamnet. Och BTW så det finns samma index på programsidan och på skalet.
Jag tror att det här bara var ett bekvämlighetstrick (i början), och som du ser i andra svar var det också väldigt praktiskt, så denna tradition fortsatte och et som API.
Svar
I grund och botten innehåller argv programnamnet så att du kan skriva felmeddelanden som prgm: file: No such file or directory
, som skulle implementeras med något så här:
fprintf( stderr, "%s: %s: No such file or directory\n", argv[0], argv[1] );
Svar
Ett annat exempel på en tillämpning av detta är detta program, som ersätter sig själv med … själv tills du skriver något som inte är ”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; }
Självklart är det ett slags konstruerat om intressant exempel, men jag tror att det kan ha verkliga användningsområden – till exempel en självuppdaterande binär, som skriver om sitt eget minnesutrymme med en ny version av sig själv som den laddade ner eller ändrade.
Exempel:
$ ./res 1 arg: 1 y arg: 2 y arg: 3 y arg: 4 y arg: 5 y arg: 6 y arg: 7 n 7 | $
Kommentarer
- Grattis till att du når 1000.
Svar
Sökvägen till programmet är argv[0]
, så att programmet kan hämta konfigurationsfiler etc. från dess installationskatalog.
Detta skulle vara omöjligt utan argv[0]
.
Kommentarer
- Att ’ inte är en särskilt bra förklaring – det finns ’ ingen anledning att vi inte kunde ’ t har standardiserats på något som
(char *path_to_program, char **argv, int argc)
till exempel - Afaik, de flesta program hämtar konfiguration från en standardplats (
~/.<program>
,/etc/<program
,$XDG_CONFIG_HOME
) och antingen ta en parameter för att ändra den eller ha en kompileringstid som bakar i en konstant till den binära.
Svar
ccache beter sig på detta sätt för att imitera olika samtal till kompilatorbinarier. ccache är en kompileringscache – hela poängen är att aldrig kompilera samma källkod två gånger utan istället returnera objektkoden från cache om möjligt.
Från ccache man-sida , ”det finns två sätt att använda ccache. Du kan antingen prefixa dina kompileringskommandon med ccache eller så kan du låta ccache maskerera som kompilatorn genom att skapa en symbolisk länk (namngiven som kompilatorn) till ccache. den första metoden är mest praktisk om du bara vill prova ccache eller använda den för vissa specifika projekt. Den andra metoden är mest användbar när du vill använda ccache för alla dina sammanställningar. ”
symlinks-metoden innebär att du kör dessa kommandon:
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 ...
… vars effekt är att ccache låter alla kommandon som annars skulle ha gått till kompilatorerna, vilket gör att ccache kan returnera en cachad fil eller skicka kommandot till själva kompilatorn.
sh
är symlink tilldash
. De beter sig annorlunda när de kallassh
eller somdash
busybox
(vanligt på räddningsskivor och liknande), då är ganska mycket allt (cp, mv, rm, ls, …) en symbolisk länk till upptagenbox.gcc
,bash
,gunzip
, större delen av resten av operativsystemet …), eftersom Linux bara är kärnan.