Dlaczego nie użyć “, które ”? Czego więc użyć?

Szukając ścieżki do pliku wykonywalnego lub sprawdzając, co by się stało, gdybyś wpisał nazwę polecenia w powłoce Uniksa, jest mnóstwo różnych narzędzi ( which, type, command, whence, where, whereis, whatis, hash itp.).

Często słyszymy, że należy unikać which. Dlaczego? Czego powinniśmy użyć zamiast tego?

Komentarze

  • Myślę, że większość argumentów przeciwko używaniu which dotyczy interaktywnego kontekstu powłoki. To pytanie jest oznaczone tagiem / przenośność. Dlatego interpretuję pytanie w tym kontekście jako „, czego użyć zamiast which, aby znaleźć pierwszy plik wykonywalny o podanej nazwie w $PATH „. Większość odpowiedzi i powodów przeciw which zajmują się aliasami, poleceniami wbudowanymi i funkcjami, które w większości rzeczywistych przenośnych skryptów powłoki są tylko przedmiotem zainteresowania akademickiego. Aliasy zdefiniowane lokalnie nie są dziedziczone ' podczas uruchamiania skryptu powłoki (chyba że źródłem go jest .).
  • @MattBianco, tak, csh (a which to nadal csh skrypt w większości reklam Unices) czyta ~/.cshrc w trybie nieinteraktywnym. To ' jest powodem, dla którego ' zauważysz, że skrypty csh zwykle zaczynają się od #! /bin/csh -f. which nie, ponieważ ma na celu podanie aliasów, ponieważ ' ma służyć jako narzędzie do (interaktywni) użytkownicy csh. Użytkownicy powłok POSIX mają command -v.
  • @rudimeier, wtedy odpowiedzią będzie zawsze, chyba że twoja powłoka to (t)csh (lub nie ' nie masz nic przeciwko, jeśli ' nie daje poprawnego wyniku), użyj type lub command -v zamiast tego . Zobacz odpowiedzi, aby dowiedzieć się, dlaczego .
  • @rudimeier, (stat $(which ls) jest błędne z kilku powodów (brak --, brakujące cudzysłowy), a nie tylko użycie which). ' d używasz stat -- "$(command -v ls)". Zakłada się, że ls jest rzeczywiście poleceniem znalezionym w systemie plików (nie jest to polecenie wbudowane w powłokę ani funkcja aliasu). which może podać złą ścieżkę (a nie ścieżkę, którą uruchomiłaby powłoka, gdybyśmy wprowadzili ls) lub alias zgodnie z definicją w konfiguracji niektórych innych powłok …
  • @rudimeier, znowu, istnieje szereg warunków, w których wiele which implementacji nie zapewniłoby nawet ls, które zostanie znalezione przez wyszukiwanie $PATH (niezależnie od tego, co ls może wywołać w Twojej powłoce). sh -c 'command -v ls' lub zsh -c 'rpm -q --whatprovides =ls' z większym prawdopodobieństwem udzielą poprawnej odpowiedzi. Chodzi o to, że which to zepsute dziedzictwo csh.

Odpowiedź

Oto wszystko, o czym nigdy nie myślałeś, że nigdy nie chciałbyś o tym wiedzieć:

Podsumowanie

Aby uzyskać nazwa ścieżki do pliku wykonywalnego w skrypcie powłoki podobnym do Bournea (jest kilka zastrzeżeń; patrz poniżej):

ls=$(command -v ls) 

Aby dowiedzieć się, czy dane polecenie istnieje:

if command -v given-command > /dev/null 2>&1; then echo given-command is available else echo given-command is not available fi 

Po zachęcie interaktywnej powłoki podobnej do Bournea:

type ls 

Polecenie which jest zepsutym dziedzictwem C-Shell i lepiej pozostawić je w spokoju w powłokach podobnych do Bournea.

Użyj przypadków

Tam „to rozróżnienie między szukaniem tych informacji jako części skryptu a interaktywnie w zachęcie powłoki.

Typowy przypadek użycia w linii poleceń wygląda następująco: to polecenie zachowuje się dziwnie, czy używam właściwy? Co dokładnie się stało, gdy wpisałem mycmd? Czy mogę dokładniej przyjrzeć się temu, co to jest?

W takim przypadku chcesz wiedzieć, co robi twoja powłoka, gdy wywołujesz polecenie bez faktycznego wywoływania polecenia.

W skryptach powłoki wygląda to zupełnie inaczej. W skrypcie powłoki nie ma powodu, dla którego miałbyś chcieć wiedzieć, gdzie lub czym jest polecenie, jeśli chcesz tylko je uruchomić. Ogólnie rzecz biorąc, to, co chcesz wiedzieć, to ścieżka do pliku wykonywalnego, dzięki czemu możesz uzyskać z niego więcej informacji (na przykład ścieżkę do innego pliku względem tego lub odczytać informacje z zawartości pliku wykonywalnego w tej ścieżce).

W trybie interaktywnym możesz chcieć wiedzieć o wszystkich poleceniach my-cmd dostępnych w systemie, rzadko w skryptach.

Większość dostępnych narzędzi (jak to często bywa) została zaprojektowana do użytku interaktywnego.

Historia

Najpierw trochę historii.

Wczesne powłoki Uniksa aż do późnych lat 70. nie miały żadnych funkcji ani aliasów. Tylko tradycyjne wyszukiwanie plików wykonywalnych w $PATH. csh wprowadził aliasy około 1978 r. (chociaż csh został po raz pierwszy opublikowany w 2BSD, maj 1979), a także przetwarzanie .cshrc dla użytkowników w celu dostosowania powłoki (każda powłoka, jak csh , czyta .cshrc, nawet jeśli nie jest interaktywny, jak w skryptach).

Podczas gdy powłoka Bourne została po raz pierwszy wydana w Unix V7 wcześniej w 1979 roku, obsługa funkcji została dodana tylko później (1984 w SVR2), a poza tym nigdy nie miał on żadnego pliku rc (.profile służy do konfiguracji twojego środowiska, a nie powłoki per se ).

csh stał się dużo bardziej popularny niż powłoka Bournea, ponieważ (chociaż miał okropnie gorszą składnię niż Bournea) dodawał wiele wygodniejszych i przyjemniejszych funkcji do interaktywnego użytku.

W 3BSD (1980), which skrypt csh został dodany dla użytkowników csh, aby pomóc zidentyfikować plik wykonywalny. Nie jest to prawie inny skrypt, który można znaleźć jako which na wielu obecnie komercyjnych Uniksach (takich jak Solaris, HP / UX, AIX lub Tru64).

Ten skrypt odczytuje ~/.cshrc użytkownika (tak jak wszystkie skrypty csh, chyba że są wywoływane z csh -f) i wyszukują podane nazwy poleceń na liście aliasów i w $path (tablicy, którą csh utrzymuje na podstawie $PATH).

Proszę bardzo: which zajął pierwsze miejsce w najpopularniejszej wówczas powłoce (a csh był nadal popularny do połowy Lat 90.), co jest głównym powodem, dla którego zostało udokumentowane w książkach i nadal jest szeroko stosowane.

Pamiętaj, że nawet dla użytkownika csh which skrypt csh niekoniecznie Ci to daje właściwe informacje. Pobiera aliasy zdefiniowane w ~/.cshrc, a nie te, które mogły zostać zdefiniowane później w monicie lub na przykład przez source w innym csh i (choć to nie byłby dobry pomysł) PATH może zostać przedefiniowane w ~/.cshrc.

Uruchomienie tego polecenia which z powłoki Bournea nadal spowoduje wyszukanie aliasów zdefiniowanych w ~/.cshrc, ale jeśli go nie masz, ponieważ nie używasz csh, prawdopodobnie dostaniesz właściwą odpowiedź.

Podobna funkcja nie została dodana do powłoka Bournea do 1984 roku w SVR2 z wbudowanym poleceniem type. Fakt, że jest on wbudowany (w przeciwieństwie do zewnętrznego skryptu) oznacza, że może dostarczyć ci odpowiednich informacji (do pewnego stopnia), ponieważ ma dostęp do wewnętrznych elementów powłoki.

Początkowe polecenie type miało podobny problem jak skrypt which, ponieważ nie zwróciło statusu zakończenia błędu, jeśli polecenie nie zostało znalezione. Ponadto w przypadku plików wykonywalnych, w przeciwieństwie do which, wypisuje coś takiego jak ls is /bin/ls zamiast tylko /bin/ls, co czyniło go mniej łatwym w użyciu w skryptach.

Uniksowa wersja 8 (nie wydana na wolności) powłoka Bournea miała to” s type zmieniono nazwę na whatis. A Plan9 (niegdyś następca Uniksa) powłoka rc (i jej pochodne, takie jak akanga i es), również mają whatis.

Powłoka Korna (podzbiór, w którym Definicja POSIX sh jest oparta na), opracowana w połowie lat 80., ale nie była szeroko dostępna przed 1988 rokiem, dodała wiele funkcji csh ( edytor linii, aliasy …) na wierzchu powłoki Bournea. Dodał własne wbudowane whence (oprócz type), które wymagało kilku opcji (-v w celu udostępnienia pełnych danych wyjściowych type oraz -p w celu wyszukania tylko plików wykonywalnych (nie aliasów / funkcji …)) .

Przypadkowo z powodu zamieszania związanego z kwestiami praw autorskich między AT & T i Berkeley, pojawiło się kilka implementacji powłoki wolnego oprogramowania pod koniec lat 80-tych wczesnych 90-tych.Cała powłoka Almquist (ash, która ma zastąpić powłokę Bournea w BSD), implementacja domeny publicznej ksh (pdksh), bash (sponsorowane przez FSF), zsh wyszedł między 1989 a 1991.

Ash, chociaż miał być zamiennikiem powłoki Bournea, nie miał wbudowanego type aż do dużo później (w NetBSD 1.3 i FreeBSD 2.3 ), chociaż miał hash -v. OSF / 1 /bin/sh miał wbudowaną type, która zawsze zwrócono 0 do OSF / 1 v3.x. bash nie „nie dodałem whence, ale dodano -p opcja type, aby wydrukować ścieżkę (type -p wyglądałoby następująco: whence -p) i -a, aby zgłosić wszystkie pasujące polecenia. tcsh utworzono which wbudowane i dodano polecenie where działające jak bash” s type -a. zsh ma je wszystkie.

fish shell (2005) ma polecenie type zaimplementowane jako funkcję.

which W międzyczasie skrypt csh został usunięty z NetBSD (ponieważ został wbudowany w tcsh i nie był zbyt użyteczny w innych powłokach), a funkcjonalność dodana do whereis (po wywołaniu jako which, whereis zachowuje się jak which z tą różnicą, że wyszukuje pliki wykonywalne tylko w $PATH). W OpenBSD i FreeBSD which również zostało zmienione na napisane w C, które wyszukuje polecenia tylko w $PATH .

Implementacje

Istnieją dziesiątki implementacji which co mmand w różnych Uniksach o różnej składni i zachowaniu.

W Linuksie (oprócz wbudowanych w tcsh i zsh ) znajdujemy kilka realizacji. Na przykład w ostatnich systemach Debian jest to „prosty skrypt powłoki POSIX, który szuka poleceń w $PATH.

busybox ma również polecenie which.

Istnieje GNU which, który jest prawdopodobnie najbardziej ekstrawagancki. Próbuje rozszerzyć to, co skrypt which csh zrobił na inne powłoki: możesz mu powiedzieć, jakie są twoje aliasy i funkcje, aby mógł dać masz lepszą odpowiedź (i uważam, że niektóre dystrybucje Linuksa ustawiają wokół tego aliasy globalne dla bash, aby to zrobić).

zsh ma kilka operatorów do rozwinięcia do ścieżki plików wykonywalnych: operator = rozwinięcie nazwy pliku i :c modyfikator rozwijania historii (tutaj stosowany do rozwinięcia parametrów ):

$ print -r -- =ls /bin/ls $ cmd=ls; print -r -- $cmd:c /bin/ls 

zsh, w tworzy również tablicę skrótów poleceń jako tablicę asocjacyjną commands:

$ print -r -- $commands[ls] /bin/ls 

Narzędzie whatis (z wyjątkiem tego w powłoce Unix V8 Bourne lub Planie 9 rc / es) nie jest tak naprawdę powiązany, ponieważ służy tylko do dokumentacji (greps bazy danych whatis, czyli streszczenia strony podręcznika).

whereis dodane w 3BSD w tym samym czasie co which, chociaż zostało napisane w C, a nie csh i jest używany do jednoczesnego wyszukiwania pliku wykonywalnego, strony podręcznika i źródła, ale nie w oparciu o bieżące środowisko. Więc znowu, to odpowiada innej potrzebie.

Teraz, na standardowym froncie, POSIX określa command -v i -V polecenia (które były opcjonalne do POSIX.2008). UNIX określa polecenie type (brak opcji). To wszystko (where, which, whence nie są określone w żadnym standardzie) .

Do niektórych wersji type i command -v były opcjonalne w specyfikacji Linux Standard Base, co wyjaśnia, dlaczego na przykład niektóre starsze wersje posh (choć oparte na pdksh, które miały oba), nie miały żadnego. command -v został również dodany do niektórych implementacji powłoki Bournea (np. w Solarisie).

Stan na dziś

Obecnie status jest taki, że type i command -v są wszechobecne we wszystkich powłoki podobne do Bournea (chociaż, jak zauważył @jarno, zwróć uwagę na zastrzeżenie / błąd w bash, gdy nie jest w trybie POSIX lub niektórzy potomkowie powłoki Almquist poniżej w komentarzach). tcsh to jedyna powłoka, w której chciałbyś użyć which (ponieważ nie ma type there i which jest wbudowane).

W powłokach innych niż tcsh i zsh, which może podać ścieżkę do danego pliku wykonywalnego, o ile nie ma aliasu ani funkcji o tej samej nazwie w żadnym z naszych ~/.cshrc, ~/.bashrc lub dowolny plik startowy powłoki i nie definiujesz $PATH w swoim ~/.cshrc. Jeśli masz zdefiniowany alias lub funkcję, może, ale nie musi, o tym powiedzieć lub powiedzieć niewłaściwą rzecz.

Jeśli chcesz wiedzieć o wszystkich poleceniach o podanej nazwie, nie ma nic przenośnego. Użyjesz where w tcsh lub zsh, type -a w bash lub zsh, whence -a w ksh93 i innych powłokach , możesz użyć type w połączeniu z which -a, co może działać.

Zalecenia

Pobieranie nazwy ścieżki do pliku wykonywalnego

Teraz, aby uzyskać ścieżkę do pliku wykonywalnego w skrypcie, jest kilka zastrzeżeń:

ls=$(command -v ls) 

byłby to standardowy sposób.

Jest jednak kilka problemów:

  • Nie można poznać ścieżki do pliku wykonywalnego bez jego wykonania. Wszystko type, which, command -v … wszystkie używają heurystyki, aby znaleźć ścieżkę . W pętli przeglądają komponenty $PATH i znajdują pierwszy plik niebędący katalogiem, do którego masz uprawnienia do wykonywania. Jednak depe Jeśli chodzi o wykonanie polecenia w powłoce, wiele z nich (Bourne, AT & T ksh, zsh, ash …) po prostu wykona je w kolejności $PATH do momentu, gdy wywołanie systemowe execve nie zwróci błędu. Na przykład, jeśli $PATH zawiera /foo:/bar i chcesz wykonać ls, najpierw spróbują do wykonania /foo/ls lub jeśli to się nie powiedzie /bar/ls. Teraz wykonanie /foo/ls może się nie powieść, ponieważ nie masz uprawnień do wykonywania, ale także z wielu innych powodów, na przykład nie jest to poprawny plik wykonywalny. command -v ls zgłosi /foo/ls, jeśli masz uprawnienia do wykonywania /foo/ls, ale działasz ls może faktycznie działać /bar/ls, jeśli /foo/ls nie jest prawidłowym plikiem wykonywalnym.
  • jeśli foo to funkcja wbudowana, funkcja lub alias, command -v foo zwraca foo. W przypadku niektórych powłok, takich jak ash, pdksh lub zsh, może również zwrócić foo jeśli $PATH zawiera pusty ciąg, aw bieżącym katalogu znajduje się plik wykonywalny foo. W pewnych okolicznościach może być konieczne wzięcie tego pod uwagę. Pamiętaj na przykład, że lista poleceń wbudowanych różni się w zależności od implementacji powłoki (na przykład mount jest czasami wbudowany w busybox sh) i na przykład bash może pobierać funkcje ze środowiska.
  • if zawiera komponenty ścieżki względnej (zazwyczaj . lub pusty ciąg, który odnosi się do bieżącego katalogu, ale może być cokolwiek) w zależności od powłoki, może nie wyświetlać ścieżki bezwzględnej. Dlatego ścieżka uzyskana podczas uruchamiania przestanie działać po cd gdzie indziej.
  • Anegdotal: z powłoką ksh93, jeśli /opt/ast/bin (chociaż uważam, że ta dokładna ścieżka może się różnić w różnych systemach) jest w tobie $PATH, ksh93 udostępni kilka dodatkowych wbudowanych funkcji (chmod, cmp, cat …), ale command -v chmod zwróci /opt/ast/bin/chmod, nawet jeśli ta ścieżka nie istnieje.

Ustalanie, czy polecenie istnieje

Aby dowiedzieć się, czy dane polecenie istnieje standardowo, możesz wykonać:

if command -v given-command > /dev/null 2>&1; then echo given-command is available else echo given-command is not available fi 

Gdzie warto użyć which

(t)csh

W csh i tcsh nie masz dużego wyboru. W tcsh „W porządku, ponieważ which jest wbudowane. W csh będzie to polecenie systemowe which, które w kilku przypadkach może nie działać zgodnie z oczekiwaniami.

Znajdź polecenia tylko w niektórych powłokach

Przypadek, w którym może być sensowne użycie which, to jeśli chcesz znać ścieżkę polecenia, ignorując potencjał wbudowane lub funkcje powłoki w bash, csh (nie tcsh), dash lub Bourne skrypty powłoki, czyli powłoki, które nie mają whence -p (np. ksh lub zsh), command -ev (na przykład yash ), whatis -p (rc, akanga) lub wbudowany which (np. tcsh lub zsh) w systemach, w których which jest jest dostępny i nie jest csh.

Jeśli te warunki są spełnione, to:

echo=$(which echo) 

poda ścieżkę do pierwszego echo w $PATH (z wyjątkiem przypadków narożnych), niezależnie od tego, czy echo również jest powłoką wbudowany / alias / funkcja czy nie.

W innych powłokach wolisz:

  • zsh : echo==echo lub echo=$commands[echo] lub echo=${${:-echo}:c}
  • ksh , zsh : echo=$(whence -p echo)
  • yash : echo=$(command -ev echo)
  • rc , akanga : echo=`whatis -p echo` (uważaj na ścieżki ze spacjami)
  • ryba : set echo (type -fp echo)

Pamiętaj, że jeśli chcesz tylko uruchomić że echo polecenie, nie musisz określać jego ścieżki, możesz po prostu zrobić:

env echo this is not echoed by the builtin echo 

Na przykład z tcsh, aby zapobiec użyciu wbudowanego which:

set Echo = "`env which echo`" 

Kiedy potrzebujesz zewnętrznego polecenia

Innym przypadkiem, w którym możesz chcieć użyć which, jest sytuacja, w której faktycznie potrzebujesz polecenie zewnętrzne. POSIX wymaga, aby wszystkie polecenia wbudowane powłoki (takie jak command) były również dostępne jako polecenia zewnętrzne, ale niestety tak nie jest w przypadku command na wielu systemach. Na przykład rzadko można znaleźć polecenie command w systemach operacyjnych opartych na Linuksie, podczas gdy większość z nich ma which polecenie (choć różne z różnymi opcjami i zachowaniami).

Przypadki, w których możesz potrzebować polecenia zewnętrznego, są wszędzie tam, gdzie wykonasz polecenie bez wywoływania powłoki POSIX.

system("some command line"), popen() … funkcje C lub różnych języków wywołują powłokę w celu przeanalizowania tego wiersza poleceń, więc system("command -v my-cmd") działają w nich. Wyjątkiem byłby perl, który optymalizuje powłokę, jeśli nie widzi żadnego znaku specjalnego powłoki (innego niż spacja). Dotyczy to również operatora odwrotnego kliknięcia:

$ perl -le "print system "command -v emacs"" -1 $ perl -le "print system ":;command -v emacs"" /usr/bin/emacs 0 $ perl -e "print `command -v emacs`" $ perl -e "print `:;command -v emacs`" /usr/bin/emacs 

Dodanie tego :; powyżej wymusza perl wywołanie powłoki Tam. Używając which, nie musiałbyś używać tej sztuczki.

Komentarze

  • @Joe, which to skrypt csh na wielu komercyjnych Unikach. Powód jest historyczny, że ' to dlaczego podałem historię, aby ludzie zrozumieli, skąd się wzięła, dlaczego ludzie przyzwyczaili się do jej używania i dlaczego tak naprawdę ' nie ma powodu, żebyś go używał. I tak, niektórzy używają (t) csh. Nie wszyscy jeszcze używają Linuksa.
  • Po przeczytaniu tego posta znalazłem wiele kontekstu dla odpowiedzi, ale nie znalazłem samej odpowiedzi.W którym miejscu w tym poście faktycznie jest napisane, dlaczego nie używać which, w przeciwieństwie do rzeczy, których możesz próbować użyć which do zrobienia, historia which, implementacje which, inne polecenia do wykonania powiązanych zadań lub powody do faktycznego użycia which? Dlaczego inne polecenia są lepsze ? Czym różnią się od which? Jak uniknąć jego pułapek? W tej odpowiedzi więcej słów opisuje problemy z alternatywami niż problemy z which.
  • command jest opisane według POSIX.
  • @St é phaneChazelas Jeśli utworzę nowy plik przez touch /usr/bin/mytestfile, a następnie uruchomię , poda ścieżkę (podczas gdy which mytestfile nie).
  • @jarno, o tak, ' w prawo. bash osadzi się na pliku, który nie jest wykonywalny, jeśli nie może ' znaleźć wykonywalnego, więc ' s ” OK ” (choć w praktyce wolałoby się command -v / type zwraca błąd), ponieważ ' jest poleceniem, które będzie próbowało wykonać po uruchomieniu mytestfile, ale zachowanie dash jest błędne, tak jakby ' nie było wykonywalne cmd przed plikiem wykonywalnym, command -v zwraca ten, który nie jest wykonywalny, podczas gdy wykonanie cmd spowodowałoby wykonanie pliku wykonywalnego (niewłaściwy jeden jest również hashowany). FreeBSD sh (również oparty na ash) ma ten sam błąd. zsh, yash, ksh, mksh, bash as sh są w porządku.

Answer

Powody, dla których można nie chcę używać which zostało już wyjaśnione, ale oto kilka przykładów na kilku systemach, w których which faktycznie zawodzi.

W powłokach typu Bourne „porównujemy wynik which z wynikiem type (type jako wbudowana powłoka ma być podstawową prawdą, ponieważ to powłoka mówi nam, jak wywoła polecenie).

Wiele przypadków jest corner sprawy, ale pamiętaj, że which / type są często używane w narożnych przypadkach (aby znaleźć odpowiedź do nieoczekiwanego zachowania, takiego jak: dlaczego do licha to polecenie zachowuje się w ten sposób, do którego dzwonię? ).

Większość systemów, większość powłok podobnych do Bournea: funkcje

Najbardziej oczywisty przypadek dotyczy funkcji:

$ type ls ls is a function ls () { [ -t 1 ] && set -- -F "$@"; command ls "$@" } $ which ls /bin/ls 

Powodem jest to, że which informuje tylko o plikach wykonywalnych, a czasem o aliasach (choć nie zawsze są to aliasy twojej powłoki), a nie funkcje.

GNU, którego strona podręcznika ma zepsutą (ponieważ zapomnieli zacytować $@) przykład, jak używać go również do raportowania funkcji, ale tak jak w przypadku aliasy, ponieważ nie implementuje parsera składni powłoki, łatwo go oszukać:

$ which() { (alias; declare -f) | /usr/bin/which --tty-only --read-alias --read-functions --show-tilde --show-dot "$@";} $ f() { echo $"\n}\ng ()\n{ echo bar;\n}\n" >> ~/foo; } $ type f f is a function f () { echo " } g () { echo bar; } " >> ~/foo } $ type g bash: type: g: not found $ which f f () { echo " } $ which g g () { echo bar; } 

Większość systemów, większość powłok podobnych do Bournea: wbudowane

Innym oczywistym przypadkiem są polecenia wbudowane lub słowa kluczowe, ponieważ which będąc poleceniem zewnętrznym nie ma sposobu, aby dowiedzieć się, które wbudowane funkcje ma Twoja powłoka (i niektóre powłoki, takie jak zsh, bash lub ksh mogą ładować elementy wbudowane dynamicznie):

$ type echo . time echo is a shell builtin . is a shell builtin time is a shell keyword $ which echo . time /bin/echo which: no . in (/bin:/usr/bin) /usr/bin/time 

(to nie ma zastosowania do zsh, gdzie which jest wbudowane)

Solaris 10, AIX 7.1, HP / UX 11i, Tru64 5. 1 i wiele innych:

$ csh % which ls ls: aliased to ls -F % unalias ls % which ls ls: aliased to ls -F % ksh $ which ls ls: aliased to ls -F $ type ls ls is a tracked alias for /usr/bin/ls 

Dzieje się tak, ponieważ w większości komercyjnych Uniksów which (jak w oryginalnej implementacji na 3BSD) to csh skrypt odczytujący ~/.cshrc. Aliasy, które będzie raportować, to te zdefiniowane tam niezależnie od aliasów, które aktualnie zdefiniowałeś i niezależnie od powłoki, której faktycznie używasz.

W HP / UX lub Tru64:

% echo "setenv PATH /bin:/usr/bin" >> ~/.cshrc % setenv PATH ~/bin:/bin:/usr/bin % ln -s /bin/ls ~/bin/ % which ls /bin/ls 

(wersje Solaris i AIX rozwiązały ten problem, zapisując $path przed przeczytaniem ~/.cshrc i przywrócenie go przed wyszukaniem poleceń)

$ type "a b" a b is /home/stephane/bin/a b $ which "a b" no a in /usr/sbin /usr/bin no b in /usr/sbin /usr/bin 

Lub:

$ d="$HOME/my bin" $ mkdir "$d"; PATH=$PATH:$d $ ln -s /bin/ls "$d/myls" $ type myls myls is /home/stephane/my bin/myls $ which myls no myls in /usr/sbin /usr/bin /home/stephane/my bin 

(oczywiście będąc skryptem csh, nie można oczekiwać, że będzie działał z argumentami zawierającymi spacje …)

CentOS 6.4, bash

$ type which which is aliased to `alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde" $ alias foo=": "|test|"" $ which foo alias foo=": "|test|"" /usr/bin/test $ alias $"foo=\nalias bar=" $ unalias bar -bash: unalias: bar: not found $ which bar alias bar=" 

W tym systemie w całym systemie zdefiniowany jest alias, który otacza polecenie GNU which.

Fałszywe wyjście jest spowodowane tym, że which odczytuje dane wyjściowe bash „s alias ale nie „nie wie, jak to poprawnie przeanalizować i używa heurystyki (jeden alias na wiersz, szuka pierwszego znalezionego polecenia po |, ;, & …)

Najgorsze w CentOS jest to, że zsh ma idealnie dobre wbudowane polecenie which, ale CentOS zdołał je złamać, zastępując je niedziałającym aliasem do GNU which.

Debian 7.0, ksh93:

(chociaż dotyczy większości systemów z wieloma powłokami)

$ unset PATH $ which which /usr/local/bin/which $ type which which is a tracked alias for /bin/which 

W Debianie /bin/which to skrypt /bin/sh. W moim przypadku sh jest dash, ale jest tak samo, gdy jest bash.

Nierozstawione PATH nie wyłącza wyszukiwania PATH, ale oznacza używanie systemowego „s domyślna PATH , na którą niestety w Debianie nikt się nie zgadza (dash i bash mają /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin, zsh has /bin:/usr/bin:/usr/ucb:/usr/local/bin, ksh93 ma /bin:/usr/bin, mksh ma /usr/bin:/bin ($(getconf PATH)), execvp() (jak w env) ma :/bin:/usr/bin (tak, najpierw szuka w bieżącym katalogu!)) .

Z tego powodu which źle to widzi powyżej, ponieważ używa domyślnego dash „identyfikatora

, który różni się od ksh93 „s

Nie t lepiej z GNU which, który raportuje:

which: no which in ((null)) 

(co ciekawe, rzeczywiście istnieje /usr/local/bin/which w moim systemie, który w rzeczywistości jest akanga skryptem dostarczonym z akanga (rc pochodna powłoki, gdzie domyślnym PATH jest /usr/ucb:/usr/bin:/bin:.))

bash, dowolny system:

Ten Chris nawiązuje do swojej odpowiedzi :

$ PATH=$HOME/bin:/bin $ ls /dev/null /dev/null $ cp /bin/ls bin $ type ls ls is hashed (/bin/ls) $ command -v ls /bin/ls $ which ls /home/chazelas/bin/ls 

Również po ręcznym wywołaniu hash:

$ type -a which which is /usr/local/bin/which which is /usr/bin/which which is /bin/which $ hash -p /bin/which which $ which which /usr/local/bin/which $ type which which is hashed (/bin/which) 

Teraz sprawa, w której which a czasami type niepowodzenie:

$ mkdir a b $ echo "#!/bin/echo" > a/foo $ echo "#!/" > b/foo $ chmod +x a/foo b/foo $ PATH=b:a:$PATH $ which foo b/foo $ type foo foo is b/foo 

Teraz z niektórymi powłokami:

$ foo bash: ./b/foo: /: bad interpreter: Permission denied 

Z innymi:

$ foo a/foo 

Ani which ani type może z góry wiedzieć, że b/foo nie może b został stracony. Niektóre powłoki, takie jak bash, ksh lub yash, podczas wywoływania foo rzeczywiście spróbuje uruchomić b/foo i zgłosić błąd, podczas gdy inne (np. zsh, ash, csh, Bourne, tcsh) będą działać a/foo po niepowodzeniu wywołania systemowego execve() w b/foo.

Komentarze

  • mksh w rzeczywistości używa czegoś innego dla domyślnego $PATH: first , używana jest stała czasu kompilacji systemu operacyjnego _PATH_DEFPATH (najczęściej w BSD), a następnie confstr(_CS_PATH, …) (POSIX), a jeśli oba nie istnieją lub zawiodą, używany jest /bin:/usr/bin:/sbin:/usr/sbin.
  • W pierwszym przykładzie, nawet jeśli ls jest funkcją, z której korzysta g ls z PATH. I which wystarczy, aby powiedzieć, który z nich jest używany /usr/bin/ls czy /usr/local/bin/ls. Nie ' nie widzę ” Dlaczego nie użyć tego ” ….
  • @rudimeier, That which ls da mi /bin/ls niezależnie od tego, czy ls wywołania funkcji /bin/ls lub /opt/gnu/bin/ls lub dir albo nic. IOW, which (te implementacje, IMMV) podaje coś nieistotnego
  • @St é phaneChazelas. Nie nie nie. Już wiem , że mój ls jest funkcją. Wiem , że moja funkcja ls wywołuje ls z PATH. Teraz which mówi mi, gdzie jest plik. Widzisz tylko jeden przypadek użycia: ” Co moja powłoka zrobiłaby z tym poleceniem. ” W tym przypadku użycia which jest błędne, prawidłowe.Ale są też inne przypadki użycia, w których (GNU) which jest dokładnie tym właściwym.
  • @rudimeter, zależy od which implementacja. Niektórzy powiedzą Ci, że ' jest aliasem (jeśli masz skonfigurowany alias lub jeśli w Twoim domu jest ~/.cshrc), który ma taki alias), niektóre podają ścieżkę, ale w pewnych warunkach jest to niewłaściwa. sh -c 'command -v ls', chociaż nie jest doskonałe, to jednak bardziej prawdopodobne jest, że da ci właściwą odpowiedź na to inne wymaganie (i jest również standardem).

Odpowiedź

Jedna rzecz, o której (z mojego szybkiego przeglądu) wydaje się, że Stephane nie wspomniał, to to, że which ma nie mam pojęcia o tablicy hash Twojej powłoki. W rezultacie może zwrócić wynik, który nie jest reprezentatywny dla tego, co faktycznie jest uruchomione, co czyni go nieefektywnym w debugowaniu.

Odpowiedź

Zwykle wzdrygam się, gdy to pytanie jest zalecane niczego niepodejrzewającym użytkownikom, ponieważ bezzasadne atakowanie which nie jest dla nikogo przydatne.

Jeśli which działa dobrze i zapewnia poprawną odpowiedź na niektóre zadania, zgodnie z moto Uniksa: zrób jedną rzecz, zrób to dobrze , dlaczego which zostanie zbanowany?

Pytanie powinno zatem brzmieć, co działa dobrze i dobrze wykonuje określone zadanie?

Po pierwsze, zewnętrzne narzędzie w / bin / które w Debianie jest skryptem powłoki, którego celem jest tylko wyświetlenie w ścieżce plików wykonywalnych o podanej nazwie. Uważam, że which prawidłowo wykonuje swój zamierzony cel. Nie ładuje żadnych aliasów, żadnych funkcji, nic z powłoki, po prostu wyświetla pierwsze (lub wszystkie) pliki wykonywalne o podanej nazwie w PATH. oznaczające znalezienia pliku o takiej samej nazwie jak podana jest czymś, co użytkownik powinien sam (on) wymyślić siebie.

Tak, inne which implementacje mogą (i zwykle mają) mieć określone problemy.

Odpowiedź

Często słyszymy o tym, czego należy unikać. Dlaczego? Czego powinniśmy użyć zamiast tego?

Nigdy tego nie słyszałem. Proszę podać konkretne przykłady. Martwiłbym się twoją dystrybucją Linuksa i zainstalowanymi pakietami oprogramowania, ponieważ to skąd pochodzi which!

SLES 11.4 x86-64

w tcsh w wersji 6.18.01:

> which which which: shell built-in command. 

w wersji bash 3.2-147:

> which which /usr/bin/which > which -v GNU which v2.19, Copyright (C) 1999 - 2008 Carlo Wood. GNU which comes with ABSOLUTELY NO WARRANTY; This program is free software; your freedom to use, change and distribute this program is protected by the GPL. 

which jest częścią util-linux to standardowy pakiet dystrybuowany przez Linux Kernel Organization do użytku jako część systemu operacyjnego Linux. Zawiera również te inne pliki

/bin/dmesg /bin/findmnt /bin/logger /bin/lsblk /bin/more /bin/mount /bin/umount /sbin/adjtimex /sbin/agetty /sbin/blkid /sbin/blockdev /sbin/cfdisk /sbin/chcpu /sbin/ctrlaltdel /sbin/elvtune /sbin/fdisk /sbin/findfs /sbin/fsck /sbin/fsck.cramfs /sbin/fsck.minix /sbin/fsfreeze /sbin/fstrim /sbin/hwclock /sbin/losetup /sbin/mkfs /sbin/mkfs.bfs /sbin/mkfs.cramfs /sbin/mkfs.minix /sbin/mkswap /sbin/nologin /sbin/pivot_root /sbin/raw /sbin/sfdisk /sbin/swaplabel /sbin/swapoff /sbin/swapon /sbin/switch_root /sbin/wipefs /usr/bin/cal /usr/bin/chrp-addnote /usr/bin/chrt /usr/bin/col /usr/bin/colcrt /usr/bin/colrm /usr/bin/column /usr/bin/cytune /usr/bin/ddate /usr/bin/fallocate /usr/bin/flock /usr/bin/getopt /usr/bin/hexdump /usr/bin/i386 /usr/bin/ionice /usr/bin/ipcmk /usr/bin/ipcrm /usr/bin/ipcs /usr/bin/isosize /usr/bin/line /usr/bin/linux32 /usr/bin/linux64 /usr/bin/look /usr/bin/lscpu /usr/bin/mcookie /usr/bin/mesg /usr/bin/mkzimage_cmdline /usr/bin/namei /usr/bin/rename /usr/bin/renice /usr/bin/rev /usr/bin/script /usr/bin/scriptreplay /usr/bin/setarch /usr/bin/setsid /usr/bin/setterm /usr/bin/tailf /usr/bin/taskset /usr/bin/time /usr/bin/ul /usr/bin/uname26 /usr/bin/unshare /usr/bin/uuidgen /usr/bin/wall /usr/bin/whereis /usr/bin/which /usr/bin/write /usr/bin/x86_64 /usr/sbin/addpart /usr/sbin/delpart /usr/sbin/fdformat /usr/sbin/flushb /usr/sbin/freeramdisk /usr/sbin/klogconsole /usr/sbin/ldattach /usr/sbin/partx /usr/sbin/rcraw /usr/sbin/readprofile /usr/sbin/rtcwake /usr/sbin/setctsid /usr/sbin/tunelp 

mój util-linux to wersja 2.19. Informacje o wydaniu można łatwo znaleźć w wersji 2.13 z datą (28 sierpnia 2007). Nie jestem pewien, jaki był tego cel lub cel, ale z pewnością nie uzyskał odpowiedzi w tej długiej sprawie, przegłosowanej 331 razy.

Komentarze

  • Zwróć uwagę, jak pytanie nie wspomina o tym, do jakiego Uniksa się odnosi. Linux jest tylko jednym z nielicznych.
  • Jak pokazuje Twój which -v, ' jest GNU, który (ekstrawagancki jeden wymieniony w drugiej odpowiedzi i nie jest w żaden sposób specyficzny dla Linuksa), a nie util-linux, który AFAIK nigdy nie zawierał narzędzia which. util-linux 2.19 pochodzi z 2011 r., a GNU 2.19 pochodzi z 2008 r.

Dodaj komentarz

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