Dlaczego argv zawiera nazwę programu?

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

  • jak mv i cp?
  • W Debianie sh jest dowiązaniem symbolicznym do dash. Zachowują się inaczej, gdy są nazywane sh lub dash
  • @AlexejMagura Jeśli używasz czegoś takiego jak busybox (powszechne na dyskach ratunkowych itp.), A następnie prawie wszystko (cp, mv, rm, ls, …) jest symbolicznym dowiązaniem do busybox.
  • Dla mnie ' jest to naprawdę trudne do zignorowania, więc ' ll powiedz to: prawdopodobnie masz na myśli ” GNU ” programy (gcc, bash, gunzip, większość reszty systemu operacyjnego …), ponieważ Linux to tylko jądro.
  • @ wizzwizz4 Co ' jest nie tak z ” Typowymi programami dla systemów Unix / Linux „? Czytam to jak ” Typowe programy działające w systemie Unix / Linux „. To ' jest o wiele lepsze niż twoje ograniczenie do niektórych programów GNU. Dennis Ritchie z pewnością nie używał żadnych programów GNU. Przy okazji jądro Hurd jest przykładem programu GNU, który nie ma głównej funkcji …

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?

Komentarze

  • Przykładami takich programów są bunzip2, bzcat i bzip2, 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] jest sh. Działa jako powłoka logowania, gdy argv[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 do systemctl .
  • i tak dalej.

Komentarze

  • Kolejny to sendmail i mail. 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). I tex, 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 , gdzie exec pobiera odniesienia do etykiety 2 i etykiety 1, a pod etykietą 2: pojawia się etc/init\0, a na etykiecie 1: pojawia się jako odniesienie do etykiety 2 i kończące zero), czyli w zasadzie to, co execve robi dzisiaj minus envp.
  • execv i execl 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 :

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

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

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *