Typowe programy Unix / Linux akceptują dane wejściowe wiersza poleceń jako liczbę argumentów (int argc
) i wektor argumentów (char *argv[]
). Pierwszym elementem argv
jest nazwa programu – po której następują rzeczywiste argumenty.
Dlaczego nazwa programu jest przekazywana do pliku wykonywalnego jako argument? Czy są jakieś przykłady programów używających własnej nazwy (może to być sytuacja exec
)?
Komentarze
Odpowiedź
Na początek zwróć uwagę, że argv[0]
niekoniecznie jest nazwą programu. To jest to, co dzwoniący umieszcza w argv[0]
wywołania systemowego execve
(np. Patrz to pytanie w Stack Overflow ). (Wszystkie inne warianty exec
nie są wywołaniami systemowymi, ale interfejsami do execve
).
Załóżmy na przykład, następujące (używając execl
):
execl("/var/tmp/mybackdoor", "top", NULL);
/var/tmp/mybackdoor
jest co jest wykonywane, ale argv[0]
jest ustawione na top
, a to właśnie ps
lub ( real) top
zostanie wyświetlony. Zobacz tę odpowiedź na U & L SE, aby uzyskać więcej informacji.
Ustawianie wszystkich opcji odkładając to na bok: przed pojawieniem się wymyślnych systemów plików, takich jak /proc
, argv[0]
był jedynym sposobem, w jaki proces mógł poznać swoją własną nazwę. Do czego by to się przydało?
- Niektóre programy dostosowują swoje zachowanie w zależności od nazwy, pod jaką zostały wywołane (zwykle za pomocą linków symbolicznych lub twardych, na przykład BusyBox ”; kilka innych przykładów znajduje się w innych odpowiedziach na to pytanie).
- Ponadto usługi, demony i inne programy, które logują się przez syslog, często dodają swoje nazwy do wpisy dziennika; bez tego śledzenie zdarzeń stałoby się prawie niewykonalne.
Komentarze
- Przykładami takich programów są
bunzip2
,bzcat
ibzip2
, dla których pierwsze dwa są dowiązaniami symbolicznymi do trzeciego. - @Ruslan Co ciekawe
zcat
nie jest dowiązaniem symbolicznym. Wydaje się, że unikają wad tej techniki, używając zamiast tego skryptu powłoki. Jednak nie wypisują pełnego--help
, ponieważ ktoś, kto dodał opcje do programu gzip, zapomniał o pliku main tain zcat też. - Odkąd pamiętam, standardy kodowania GNU zniechęcały do używania argv [0] do zmiany zachowania programu (sekcja ” Standardy interfejsów Ogólnie ” w aktualnej wersji ).
gunzip
to historyczny wyjątek. - busybox to kolejny doskonały przykład. Można go wywołać za pomocą 308 różnych nazw w celu wywołania różnych poleceń: busybox.net/downloads/BusyBox.html#commands
- Wiele, wiele więcej programów również umieszcza swoje
argv[0]
w danych wyjściowych użycia / pomocy zamiast na stałe zakodować swoje nazwy. Niektóre pełne, inne tylko podstawowa nazwa.
Odpowiedź
Mnóstwo:
- Bash działa w trybie POSIX , gdy
argv[0]
jestsh
. Działa jako powłoka logowania, gdyargv[0]
zaczyna się od-
. - Vim zachowuje się inaczej, gdy jest uruchomiony jako
vi
,view
,evim
,eview
,ex
,vimdiff
itd. - Busybox, jak już wspomniano.
- W systemach z systemd jako init,
shutdown
,reboot
itp. Są dowiązania symboliczne dosystemctl
. - i tak dalej.
Komentarze
- Kolejny to
sendmail
imail
. Każdy unixowy MTA jest dostarczany z dowiązaniem symbolicznym dla tych dwóch poleceń i jest zaprojektowany do emulacji oryginalnego zachowania ', gdy jest tak nazywany, co oznacza, że każdy program unixowy, który musi wysyłać pocztę, wie dokładnie jak mogą to zrobić. - inny częsty przypadek:
test
i[
: gdy dzwonisz do pierwszego , obsługuje błąd, jeśli ostatnim argumentem jest]
. (w stabilnej wersji Debiana te polecenia to dwa różne programy, ale poprzednie wersje i MacOs nadal używają tego samego programu). Itex
,latex
i tak dalej: plik binarny jest taki sam, ale patrząc, jak został wywołany, wybiera właściwy konfiguracji .init
jest podobne. - Powiązane,
[
uważa to za błąd, jeśli ostatni argument to nie]
. - Myślę, że to odpowiada na drugie pytanie, ale nie na pierwsze. Bardzo wątpię, że jakiś projektant systemu operacyjnego usiadł i powiedział » Hej, byłoby fajnie, gdybym miał ten sam program wykonujący różne rzeczy na podstawie jego nazwy wykonywalnej. Myślę, że ' umieszczę nazwę w tablicy argumentów. «
- @Joey Tak, sformułowanie ma to pokazać (P: ” Czy są jakieś …? ” A: ” Mnóstwo: … „)
Odpowiedź
Historycznie argv
to po prostu tablica wskaźników do „słów” w wierszu poleceń, więc warto zacząć od pierwszego „słowa”, którym jest nazwa programu.
Jest też sporo programów, które zachowują się inaczej w zależności od nazwy użytej do ich wywołania, więc możesz po prostu utworzyć różne linki do nich i uzyskać różne „polecenia”. najbardziej ekstremalnym przykładem, jaki przychodzi mi do głowy, jest busybox , które działa jak kilkadziesiąt różnych „poleceń” w zależności od tego, jak nazywa się .
Edytuj
: Odniesienia do pierwszej edycji systemu Unix, zgodnie z życzeniem
Można zobaczyć np. z głównej funkcji cc
, że argc
i argv
były już używane. Powłoka kopiuje argumenty do parbuf
wewnątrz newarg
części pętla, traktując samo polecenie w taki sam sposób, jak argumenty. (Oczywiście później wykonuje tylko pierwszy argument, czyli nazwę polecenia). Wygląda na to, że execv
i krewni wtedy nie istnieli.
Komentarze
- proszę dodać odniesienia, które zrób kopię zapasową.
- Z szybkiego przeglądania
exec
bierze nazwę polecenia do wykonania i tablicę zakończonych zerem wskaźników znaków (najlepiej widocznych w minnie.tuhs.org/cgi-bin/utree.pl?file=V1/u0.s , gdzieexec
pobiera odniesienia do etykiety 2 i etykiety 1, a pod etykietą2:
pojawia sięetc/init\0
, a na etykiecie1:
pojawia się jako odniesienie do etykiety 2 i kończące zero), czyli w zasadzie to, coexecve
robi dzisiaj minusenvp
. -
execv
iexecl
istnieją ” na zawsze ” (tj. od początku do połowy lat siedemdziesiątych) –execv
było wywołaniem systemowym i była funkcją biblioteczną, która ją wywołała.execve
nie ' wtedy nie istniało, ponieważ nie istniało wówczas środowisko '. Pozostali członkowie rodziny zostali dodani później. - @ G-Man Czy możesz wskazać mi adres
execv
w źródle v1, które połączyłem? Po prostu ciekawy.
Odpowiedź
Przypadki użycia:
Możesz użyć nazwy programu , aby zmienić zachowanie programu .
Na przykład możesz utworzyć kilka dowiązań symbolicznych do rzeczywistego pliku binarnego.
Jednym słynnym przykładem zastosowania tej techniki jest projekt busybox, który instaluje tylko jeden plik binarny i wiele dowiązań do niego. (ls, cp, mv itp.). Robią to , aby zaoszczędzić miejsce w pamięci , ponieważ ich celem są małe urządzenia wbudowane.
To także używany w setarch
z 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
Tutaj używają tej techniki w zasadzie , aby uniknąć wielu zduplikowanych plików źródłowych lub po prostu zachować czytelność źródeł.
Innym przypadkiem użycia byłby program, który wymaga aby załadować niektóre moduły lub dane w czasie wykonywania. Posiadanie ścieżki programu umożliwia ładowanie modułów ze ścieżki zależnej od lokalizacji programu .
Ponadto wiele programów wyświetla komunikaty o błędach, w tym nazwę programu .
Dlaczego :
- Ponieważ jest to konwencja POSIX (
man 3p execve
):
argv to tablica ciągów argumentów przekazywana do nowego programu. Zgodnie z konwencją, pierwszy z tych ciągów powinien zawierać nazwę pliku powiązaną z wykonywanym plikiem.
- To jest C standard (przynajmniej C99 i C11):
Jeśli wartość argc jest większa od zera, ciąg wskazywany przez argv [0 ] reprezentuje nazwę programu; argv [0] [0] będzie znakiem null, jeśli nazwa programu nie jest dostępna ze środowiska hosta.
Uwaga, standard C mówi: „program name „nie” nazwa pliku „.
Komentarze
- Nie ' t przerwa, jeśli dojdziesz do łącze symboliczne z innego linku symbolicznego?
- @Mehrdad, Tak, że ' jest wadą i może być mylące dla użytkownika.
- @rudimeier: Twoje ' Dlaczego ' elementy nie są tak naprawdę powodem, ' to tylko ” homunculus „, czyli po prostu nasuwa się pytanie, dlaczego norma wymaga, aby tak było.
- @ einpoklum OP ' Pytanie brzmiało: Dlaczego nazwa programu jest przekazywana do pliku wykonywalnego? Odpowiedziałem: Ponieważ standard POSIX i C nakazuje nam to zrobić. Jak myślisz, że ' nie jest tak naprawdę powodem ? Jeśli dokumenty, które ' zacytowałem, nie istniałyby, prawdopodobnie wiele programów nie przekazałoby nazwy programu.
- OP prosi o ” DLACZEGO standardy POSIX i C mówią, aby to zrobić? ” To prawda, że sformułowanie było na abstrakcyjnym poziomie, ale wydaje się jasne. Realistycznie rzecz biorąc, jedynym sposobem, aby się dowiedzieć, jest zapytać twórców.
Odpowiedź
Oprócz programów zmieniających ich zachowanie w zależności od tego, jak zostały wywołane, uważam, że argv[0]
jest przydatne przy drukowaniu użycia programu, na przykład:
printf("Usage: %s [arguments]\n", argv[0]);
Powoduje to, że komunikat użytkowania zawsze używa nazwy, za pomocą której został wywołany. Jeśli nazwa programu zostanie zmieniona, komunikat o użytkowaniu zmieni się wraz z nią. Zawiera nawet nazwę ścieżki, z którą został wywołany:
# 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]
To miły akcent, szczególnie dla małych specjalnych narzędzi / skryptów, które mogą istnieć wszędzie to miejsce.
Wydaje się, że jest to powszechna praktyka również w narzędziach GNU, zobacz ls
, na przykład:
% 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.
Komentarze
- +1. Miałem zamiar zasugerować to samo. Dziwne, że tak wiele osób skupia się na zmianie zachowania i nie wspomina prawdopodobnie najbardziej oczywistego i znacznie bardziej powszechne użycie.
Odpowiedź
Jeden uruchamia program, wpisując: program_name0 arg1 arg2 arg3 ...
.
Więc powłoka powinna już podzielić token, a pierwszy token jest już nazwą programu. A tak przy okazji, są te same indeksy po stronie programu i po stronie powłoki.
Myślę, że to była tylko wygodna sztuczka (na samym początku) i, jak widać w innych odpowiedziach, była również bardzo przydatna, więc ta tradycja była kontynuowana i s et jako API.
Odpowiedź
Zasadniczo argv zawiera nazwę programu, dzięki czemu można pisać komunikaty o błędach, takie jak prgm: file: No such file or directory
, który zostałby zaimplementowany w następujący sposób:
fprintf( stderr, "%s: %s: No such file or directory\n", argv[0], argv[1] );
Odpowiedź
Innym przykładem zastosowania tego programu jest ten program, który zastępuje się … samym sobą, dopóki nie wpiszesz czegoś, co nie jest „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; }
Oczywiście trochę wymyślony, choć interesujący przykład, ale myślę, że może mieć rzeczywiste zastosowania – na przykład samaktualizujący się plik binarny, który przepisuje własną przestrzeń pamięci z nową wersją samego siebie, którą pobrał lub zmienił.
Przykład:
$ ./res 1 arg: 1 y arg: 2 y arg: 3 y arg: 4 y arg: 5 y arg: 6 y arg: 7 n 7 | $
Źródło i trochę więcej informacji .
Komentarze
- Gratulujemy osiągnięcia 1000.
Odpowiedź
Ścieżka do programu to argv[0]
, więc program może pobrać pliki konfiguracyjne itp. z katalogu instalacyjnego.
Byłoby to niemożliwe bez argv[0]
.
Komentarze
- To ' nie jest szczególnie dobrym wyjaśnieniem – ' nie ma powodu, dla którego nie mogliśmy ' nie ustandaryzowaliśmy na przykład
(char *path_to_program, char **argv, int argc)
- Afaik, większość programów pobiera konfigurację ze standardowej lokalizacji (
~/.<program>
,/etc/<program
,$XDG_CONFIG_HOME
) i albo weź parametr, aby go zmienić, albo skorzystaj z opcji kompilacji, która wstawia stałą do pliku binarnego.
Odpowiedź
ccache zachowuje się w ten sposób, aby imitować różne wywołania plików binarnych kompilatora. ccache to pamięć podręczna kompilacji – chodzi o to, aby nigdy nie kompilować tego samego kodu źródłowego dwa razy, ale zamiast tego zwrócić kod wynikowy z pamięci podręcznej, jeśli to możliwe.
Z ccache man page , „istnieją dwa sposoby użycia ccache. Możesz albo poprzedzić swoje polecenia kompilacji ccache, albo pozwolić ccache podszywać się pod kompilator, tworząc dowiązanie symboliczne (nazwane jako kompilator) do ccache. pierwsza metoda jest najwygodniejsza, jeśli chcesz po prostu wypróbować ccache lub chcesz jej użyć do niektórych konkretnych projektów. Druga metoda jest najbardziej użyteczna, gdy chcesz używać ccache do wszystkich swoich kompilacji. ”
metoda symlinks polega na uruchomieniu tych poleceń:
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 ...
… czego skutkiem jest umożliwienie ccache przechwycenia poleceń, które w innym przypadku trafiłyby do kompilatorów, w ten sposób umożliwiając ccache zwrócenie zbuforowanego pliku lub przekazanie polecenia do właściwego kompilatora.
sh
jest dowiązaniem symbolicznym dodash
. Zachowują się inaczej, gdy są nazywanesh
lubdash
busybox
(powszechne na dyskach ratunkowych itp.), A następnie prawie wszystko (cp, mv, rm, ls, …) jest symbolicznym dowiązaniem do busybox.gcc
,bash
,gunzip
, większość reszty systemu operacyjnego …), ponieważ Linux to tylko jądro.