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

328

Patrząc na ścieżce do pliku wykonywalnego lub sprawdzanie, co by się stało, jeśli wpiszesz nazwę polecenia w powłoce Unix, istnieje mnóstwo różnych mediów ( which, type, command, whence, where, whereis, whatis, hash, etc).

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

Stéphane Chazelas
źródło
3
Myślę, że większość argumentów przeciwko użyciu whichzakłada interaktywny kontekst powłoki. To pytanie jest oznaczone / przenośne. Tak więc interpretuję pytanie w tym kontekście jako „czego użyć zamiast whichznaleźć pierwszy plik wykonywalny danej nazwy w $PATH”. Większość odpowiedzi i powodów whichprzemawiających przeciwko aliasom, wbudowanym funkcjom i funkcjom, które w większości przenośnych skryptów powłoki w świecie rzeczywistym są przedmiotem zainteresowania akademickiego. Lokalnie zdefiniowane aliasy nie są dziedziczone podczas uruchamiania skryptu powłoki (chyba że zostanie to za pomocą źródła .).
MattBianco
5
@MattBianco, tak, csh(i whichnadal jest cshskryptem na większości komercyjnych Unices) czyta, ~/.cshrcgdy nie jest interaktywny. Dlatego zauważysz, że skrypty csh zwykle zaczynają się od #! /bin/csh -f. whichnie robi tego, ponieważ ma na celu udostępnienie aliasów, ponieważ ma służyć jako narzędzie dla (interaktywnych) użytkowników csh. Użytkownicy powłok POSIX command -v.
Stéphane Chazelas
@ rudimeier, wtedy odpowiedź będzie zawsze, chyba że twoja powłoka jest (t)csh(lub nie masz nic przeciwko, jeśli nie da ci poprawnego wyniku), użyj typelub command -vzamiast tego . Zobacz odpowiedzi na pytanie dlaczego .
Stéphane Chazelas
1
@rudimeier, ( stat $(which ls)jest niepoprawny z kilku powodów (brakujące --, brakujące cytaty), nie tylko użycie which). Użyłbyś stat -- "$(command -v ls)". Zakłada lssię, że rzeczywiście jest to polecenie znalezione w systemie plików (nie wbudowane w powłokę ani funkcję aliasu). whichmoże dać ci niewłaściwą ścieżkę (nie ścieżkę, którą wykonałaby twoja powłoka, gdybyś wszedł ls) lub dać alias zdefiniowany w konfiguracji niektórych innych powłok ...
Stéphane Chazelas
1
@ rudimeier, znowu, istnieje szereg warunków, w których wiele whichimplementacji nie dałoby ci nawet tych ls, które można by znaleźć podczas wyszukiwania $PATH(niezależnie od tego, co lsmoże wywołać w twojej powłoce). sh -c 'command -v ls'lub zsh -c 'rpm -q --whatprovides =ls'częściej udzielają prawidłowej odpowiedzi. Chodzi o to, że whichjest to zepsute dziedzictwo csh.
Stéphane Chazelas

Odpowiedzi:

365

Oto wszystko, o czym nigdy nie pomyślałeś, że nigdy nie chcesz o tym wiedzieć:

Podsumowanie

Aby uzyskać nazwę ścieżki pliku wykonywalnego w skrypcie powłoki podobnym do Bourne'a (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 Bourne'a:

type ls

Ta whichkomenda jest zepsutym dziedzictwem z C-Shell i lepiej zostawić ją samą w muszlach podobnych do Bourne'a.

Przypadków użycia

Istnieje rozróżnienie między wyszukiwaniem tych informacji jako części skryptu lub interaktywnym po zachęcie powłoki.

W wierszu poleceń typowym przypadkiem użycia jest: to polecenie zachowuje się dziwnie, czy używam właściwego? Co dokładnie się stało, kiedy pisałem mycmd? Czy mogę przyjrzeć się temu, co to jest?

W takim przypadku chcesz wiedzieć, co robi twoja powłoka, kiedy 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 chciałbyś wiedzieć, gdzie lub czym jest polecenie, jeśli wszystko, co chcesz zrobić, to uruchomić je. Ogólnie rzecz biorąc, chcesz wiedzieć o ścieżce pliku wykonywalnego, dzięki czemu możesz uzyskać więcej informacji (np. Ścieżkę do innego pliku w stosunku do tego lub odczytać informacje z zawartości pliku wykonywalnego przy tej ścieżce).

Interaktywnie, może chcesz wiedzieć o wszystkich tych my-cmdpoleceń dostępnych w systemie, w skryptach, rzadko tak.

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

Historia

Najpierw trochę historii.

Wczesne powłoki uniksowe do późnych lat 70. nie miały żadnych funkcji ani aliasów. Tylko tradycyjne wyszukiwanie plików wykonywalnych w $PATH. cshwprowadzono aliasy około 1978 r. (chociaż cshpo raz pierwszy wydano w 2BSDmaju 1979 r.), a także przetwarzanie .cshrcdla użytkowników w celu dostosowania powłoki (każda powłoka, jak cshczyta się, .cshrcnawet jeśli nie jest interaktywna, jak w skryptach).

natomiast Bourne shell został po raz pierwszy wydany w Unix V7 wcześniej w 1979 roku, wsparcie funkcja została tylko dodaje dużo później (1984 w SVR2), a w każdym razie, nigdy nie miałem jakiś rcplik ( .profilejest do skonfigurowania środowiska, nie powłoce per se ).

csh zyskał znacznie większą popularność niż powłoka Bourne'a, ponieważ (choć miał strasznie gorszą składnię niż powłoka Bourne'a), dodawał wiele wygodniejszych i przyjemniejszych funkcji do interaktywnego użytku.

W 3BSD(1980) dodano whichskrypt csh dla cshużytkowników, aby pomóc zidentyfikować plik wykonywalny, i jest to prawie inny skrypt, jaki można obecnie znaleźć whichw wielu komercyjnych Unices (takich jak Solaris, HP / UX, AIX lub Tru64).

Ten skrypt odczytuje użytkownika ~/.cshrc(tak jak wszystkie cshskrypty, chyba że jest wywoływany csh -f) i wyszukuje podane nazwy poleceń na liście aliasów i $path(w tablicy, która cshutrzymuje się na podstawie $PATH).

Proszę bardzo, whichw tym czasie pojawiła się pierwsza najpopularniejsza powłoka (i cshbyła popularna do połowy lat 90.), co jest głównym powodem, dla którego została udokumentowana w książkach i jest nadal szeroko stosowana.

Zauważ, że nawet dla cshużytkownika ten whichskrypt csh niekoniecznie dostarcza ci właściwych informacji. Pobiera zdefiniowane aliasy ~/.cshrc, a nie te, które mogłeś zdefiniować później w monicie lub na przykład poprzez umieszczenie sourceinnego cshpliku, i (choć nie byłoby to dobrym pomysłem) PATHmoże zostać ponownie zdefiniowane ~/.cshrc.

Uruchomienie tego whichpolecenia z powłoki Bourne'a nadal szukałoby aliasów zdefiniowanych w twoim ~/.cshrc, ale jeśli nie masz takiego, ponieważ go nie używasz csh, prawdopodobnie nadal dostaniesz właściwą odpowiedź.

Podobna funkcjonalność została dodana do powłoki Bourne'a dopiero w 1984 roku w SVR2 z typewbudowanym poleceniem. Fakt, że jest on wbudowany (w przeciwieństwie do zewnętrznego skryptu) oznacza, że może on dostarczyć odpowiednich informacji (do pewnego stopnia), ponieważ ma dostęp do wewnętrznych elementów powłoki.

Początkowa typekomenda napotkała podobny problem jak whichskrypt, ponieważ nie zwróciła statusu wyjścia błędu, jeśli komenda nie została znaleziona. Ponadto w przypadku plików wykonywalnych, w przeciwieństwie do whichtego, wypisuje coś podobnego ls is /bin/lszamiast tego, /bin/lsco czyni go łatwiejszym w użyciu w skryptach.

Uniksowa wersja 8 (nie wydana na wolności) Powłoka Bourne'a, której typenazwa wbudowana została zmieniona na whatis. Powłoka Plan9 (niegdyś następca Uniksa) rc(i jej pochodne jak akangai es) również mają whatis.

Powłoka Korna (podzbiór, na którym opiera się definicja sh POSIX), opracowana w połowie lat 80., ale nie szeroko dostępna przed 1988 r., Dodała wiele cshfunkcji (edytor linii, aliasy ...) na wierzchu powłoki Bourne'a . Dodał własne whencewbudowane (oprócz type), które wzięło kilka opcji ( -vaby zapewnić typepełne, podobne wyjście, i -pszukać tylko plików wykonywalnych (nie aliasów / funkcji ...).

Zbieg okoliczności w związku z zawirowaniami dotyczącymi praw autorskich między AT&T i Berkeley, kilka implementacji powłoki wolnego oprogramowania pojawiło się pod koniec lat 80. na początku lat 90. Cała skorupa Almquista (jesion, która ma zastąpić powłokę Bourne'a w BSD), publiczna implementacja ksh (pdksh) bash(sponsorowana przez FSF), zshpojawiła się w latach 1989-1991.

Ash, choć miał być zamiennikiem powłoki Bourne'a, nie miał typewbudowanej wersji dużo później (w NetBSD 1.3 i FreeBSD 2.3), chociaż tak było hash -v. OSF / 1 /bin/shmiał typewbudowane narzędzie, które zawsze zwracało 0 do OSF / 1 v3.x. bashnie dodał, whenceale dodał -popcję typedrukowania ścieżki ( type -pbyłoby jak whence -p) i -azgłaszania wszystkich pasujących poleceń. tcshwykonany whichwbudowane i dodaje wherepolecenie działając jak bash„s type -a. zshma je wszystkie.

fishShell (2005) ma typepolecenia realizowane jako funkcja.

whichCsh skrypt tymczasem został usunięty z NetBSD (jak to było wbudowane w tcsh i nie wiele wykorzystania w innych powłok) oraz funkcjonalność dodana do whereis(gdy wywołany jako which, whereiszachowuje się whichpoza tym, że tylko wyszukuje pliki wykonywalne w $PATH). W OpenBSD i FreeBSD whichzmieniono również na jeden napisany w C, który wyszukuje tylko polecenia $PATH.

Realizacje

Istnieją dziesiątki implementacji whichpolecenia na różnych Uniksach o różnej składni i zachowaniu.

W systemie Linux (oprócz wbudowanych w tcshi zsh) znajdujemy kilka implementacji. Na przykład w najnowszych systemach Debian jest to prosty skrypt powłoki POSIX, który szuka poleceń w $PATH.

busyboxma również whichpolecenie.

Jest taki, GNU whichktóry jest prawdopodobnie najbardziej ekstrawagancki. Próbuje rozszerzyć to, co whichskrypt csh zrobił na inne powłoki: możesz powiedzieć, jakie są twoje aliasy i funkcje, aby dać ci lepszą odpowiedź (i wierzę, że niektóre dystrybucje Linuksa ustawiają wokół tego globalne aliasy bash) .

zshma kilka operatorów do rozszerzenia ścieżki do plików wykonywalnych: operator = rozwijania nazw plików i :cmodyfikator rozszerzania historii (tutaj stosowany do rozszerzania parametrów ):

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

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

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

whatisGospodarczy (z wyjątkiem jednego w Unix V8 Bourne shell lub planu 9 rc/ es) tak naprawdę nie jest związane, jak to tylko dokumentacja (greps bazę whatis, że jest człowiekiem strona streszczenie).

whereiszostał również dodany w 3BSDtym samym czasie, whichjakby został napisany C, csha nie służy do wyszukiwania w tym samym czasie pliku wykonywalnego, strony podręcznika i źródła, ale nie w oparciu o bieżące środowisko. Znów odpowiada to na inną potrzebę.

Teraz, na standardowym froncie, POSIX określa polecenia command -vi -V(które były opcjonalne do POSIX.2008). UNIX określa typepolecenie (brak opcji). To wszystko ( where, which, whencenie są określone w dowolnym standardzie)

Do niektórych wersji typei command -vbyły opcjonalne w specyfikacji Linux Standard Base, która wyjaśnia, dlaczego na przykład niektóre stare wersje posh(choć oparte na pdkshktórych miały obie) nie miały żadnej z nich. command -vzostał również dodany do niektórych implementacji powłoki Bourne'a (takich jak Solaris).

Status dzisiaj

Obecnie status jest taki typei command -vsą one wszechobecne we wszystkich powłokach podobnych do Bourne'a (jednak, jak zauważył @jarno, zwróć uwagę na zastrzeżenie / błąd, bashgdy nie jest w trybie POSIX lub niektórzy potomkowie powłoki Almquist poniżej w komentarzach). tcshjest jedyną powłoką, w której chciałbyś użyć which(ponieważ jej nie typema i whichjest wbudowana).

W muszli innych niż tcshi zsh, whichmożna powiedzieć, ścieżkę danego pliku wykonywalnego, tak długo jak nie ma pseudonim lub funkcja o tej samej nazwie, w każdym z naszych ~/.cshrc, ~/.bashrclub dowolny plik startowy powłoki i nie określają $PATHw twojej ~/.cshrc. Jeśli masz dla niego zdefiniowany alias lub funkcję, może ci o tym powiedzieć lub nie, lub powiedzieć coś niewłaściwego.

Jeśli chcesz wiedzieć o wszystkich poleceniach pod danym imieniem, nie ma nic przenośnego. Można by użyć wherew tcshlub zsh, type -aw bashlub zsh, whence -aw ksh93 i innych skorup, można używać typew połączeniu z which -aktórych mogą pracować.

Rekomendacje

Uzyskiwanie nazwy ścieżki do pliku wykonywalnego

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

ls=$(command -v ls)

byłby to standardowy sposób na zrobienie tego.

Istnieje jednak kilka problemów:

  • Nie można poznać ścieżki pliku wykonywalnego bez jego wykonania. Wszyscy type, which, command -v... wszystkie heurystyki użytku, aby dowiedzieć się ścieżki. Przechodzą przez $PATHkomponenty i znajdują pierwszy plik inny niż katalog, dla którego masz uprawnienia do wykonania. Jednak w zależności od powłoki, jeśli chodzi o wykonanie polecenia, wiele z nich (Bourne, AT&T ksh, zsh, ash ...) po prostu wykona je w kolejności, $PATHdopóki execvewywołanie systemowe nie powróci z błędem . Na przykład, jeśli $PATHzawiera /foo:/bari chcesz wykonać ls, najpierw spróbują wykonać /foo/lslub jeśli to się nie powiedzie /bar/ls. Teraz wykonanie/foo/lsmoże się nie powieść, ponieważ nie masz uprawnień do wykonywania, ale także z wielu innych powodów, np. nie jest to prawidłowy plik wykonywalny. command -v lszgłosi, /foo/lsjeśli masz uprawnienia do wykonywania /foo/ls, ale uruchomienie lsmoże faktycznie działać, /bar/lsjeśli /foo/lsnie jest prawidłowym plikiem wykonywalnym.
  • jeśli foojest wbudowaną funkcją lub aliasem, command -v foozwraca foo. Przy czym niektóre muszle podoba ash, pdkshczy zshmoże również wrócić foojeśli $PATHzawiera pusty ciąg i jest wykonywalny fooplik w bieżącym katalogu. Istnieją pewne okoliczności, w których może to być konieczne. Pamiętaj na przykład, że lista wbudowanych elementów różni się w zależności od implementacji powłoki (na przykład mountczasami jest wbudowana dla busyboksa sh) i na przykład bashmoże pobierać funkcje ze środowiska.
  • jeśli $PATHzawiera względne komponenty ścieżki (zazwyczaj .lub pusty ciąg, które oba odnoszą się do bieżącego katalogu, ale mogą być czymkolwiek), w zależności od powłoki, command -v cmdmoże nie generować ścieżki bezwzględnej. Tak więc ścieżka, którą uzyskasz w czasie biegu command -v, nie będzie już ważna po tobie cdgdzie indziej.
  • Niepotwierdzone: z powłoką ksh93, jeśli /opt/ast/bin(choć że dokładna ścieżka może być różna w różnych systemach wierzę) jest w was $PATH, ksh93 udostępni kilka dodatkowych builtins ( chmod, cmp, cat...), ale command -v chmodwróci /opt/ast/bin/chmodnawet jeśli ścieżka robi” istnieje.

Ustalanie, czy polecenie istnieje

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

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

Gdzie można chcieć użyć which

(t)csh

W cshi tcshnie masz dużego wyboru. W tcshjest w porządku, jak whichjest wbudowane. W cshbędzie to whichpolecenie systemowe , które może nie zrobić tego, co chcesz w kilku przypadkach.

znajdź polecenia tylko w niektórych powłokach

Przypadek, w którym może ona sensu używać whichjest, jeśli chcesz poznać ścieżkę polecenia, pomijając potencjalne poleceń wbudowanych powłoki lub funkcji bash, csh(nie tcsh), dashlub Bourneskryptów powłoki, czyli powłoki, które nie mają whence -p(jak kshlub zsh) , command -ev(jak yash), whatis -p( rc, akanga) lub wbudowany which(jak tcshlub zsh) w systemach, w których whichjest dostępny i nie jest cshskryptem.

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

echo=$(which echo)

dałby ci ścieżkę pierwszego echow $PATH(z wyjątkiem przypadków narożnych), niezależnie od tego, czy echozdarza się, że jest to wbudowana powłoka / alias / funkcja czy nie.

W innych powłokach wolisz:

  • zsh : echo==echolub echo=$commands[echo]lubecho=${${:-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)
  • ryby :set echo (type -fp echo)

Zauważ, że jeśli wszystko, co chcesz zrobić, to uruchomić to echopolecenie, nie musisz uzyskać jego ścieżki, możesz po prostu zrobić:

env echo this is not echoed by the builtin echo

Na przykład, tcshaby uniemożliwić whichużycie wbudowanego :

set Echo = "`env which echo`"

kiedy potrzebujesz zewnętrznego polecenia

Innym przypadkiem, w którym możesz chcieć użyć, whichjest to, kiedy faktycznie potrzebujesz zewnętrznego polecenia. POSIX wymaga, aby wszystkie wbudowane powłoki (podobnie jak command) były również dostępne jako polecenia zewnętrzne, ale niestety tak nie jest commandw wielu systemach. Na przykład rzadko zdarza się znaleźć commandpolecenie w systemach operacyjnych opartych na systemie Linux, podczas gdy większość z nich ma whichpolecenie (choć różne z różnymi opcjami i zachowaniami).

Przypadki, w których możesz chcieć użyć zewnętrznego polecenia, znajdowałyby się wszędzie tam, gdzie wykonywałbyś polecenie bez wywoływania powłoki POSIX.

system("some command line"), popen()... Funkcje C lub różnych językach nie wywołuje powłokę do analizowania tego wiersza polecenia, więc system("command -v my-cmd")wykonywać pracę w nich. Wyjątkiem byłby ten, perlktóry optymalizuje powłokę, jeśli nie widzi żadnego specjalnego znaku powłoki (innego niż spacja). Dotyczy to również jego operatora zwrotnego:

$ 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 zmusza perldo wywołania tam skorupy. Korzystając z tej opcji which, nie musisz używać tej sztuczki.

Stéphane Chazelas
źródło
24
@Joe, whichjest cshskryptem na wiele komercyjnych Unices. Powód jest historyczny, dlatego podałem historię, więc ludzie rozumieją, skąd się wziął, dlaczego ludzie przyzwyczaili się do korzystania z niej i dlaczego właściwie nie ma powodu, abyś z niej korzystał. I tak, niektórzy ludzie używają (t) csh. Nie wszyscy jeszcze używają Linuksa
Stéphane Chazelas
12
Po przeczytaniu tego posta znalazłem wiele kontekstów dla odpowiedzi, ale nie dla samej odpowiedzi. Gdzie w tym poście jest powiedziane, dlaczego nie używać which, w przeciwieństwie do rzeczy, które możesz próbować whichzrobić, historii which, implementacji which, innych poleceń do wykonania powiązanych zadań lub powodów, dla których faktycznie chcesz użyć which? Dlaczego inne polecenia są lepsze ? Co robią inaczej which? Jak unikają pułapek? Ta odpowiedź faktycznie wydaje więcej słów na problemy z alternatywami niż na problemy which.
user62251
1
W przeciwieństwie do tego, co twierdzi odpowiedź, command -vnie sprawdza uprawnień do wykonywania, przynajmniej jeśli wywołujesz je za pomocą argumentu czystej nazwy pliku bez ścieżki. Testowałem przez dash 0.5.8 i GNU bash 4.3.48.
jarno
2
@ StéphaneChazelas Jeśli utworzę nowy plik, touch /usr/bin/mytestfilea następnie uruchomię command -v mytestfilego, poda ścieżkę (a which mytestfilenie robi).
jarno
2
@jarno, o tak, masz rację. bashustali plik wykonywalny, jeśli nie będzie w stanie znaleźć pliku wykonywalnego, więc jest „OK” (chociaż w praktyce wolałby command -v/ typezwrócił błąd), ponieważ jest to polecenie, które próbowałby wykonać podczas uruchamiania mytestfile, ale dashzachowanie jest błędne, tak jakby cmdprzed plikiem wykonywalnym było coś niewykonywalnego , command -vzwraca ono niewykonywalne, podczas gdy wykonywanie cmdwykonałoby to (także niewłaściwe). FreeBSD sh(również oparty na ash) ma ten sam błąd. Zsh, yash, ksh, mksh, bash jak sh są OK.
Stéphane Chazelas
47

Powody, dla których można nie chcieć używać, whichzostały już wyjaśnione, ale oto kilka przykładów na kilku systemach, w których whichfaktycznie zawodzi.

W przypadku powłok podobnych do Bourne'a porównujemy dane wyjściowe whichz danymi wyjściowymi type( typejako wbudowana powłoka, ma to być podstawowa prawda, ponieważ jest to powłoka, która mówi nam, jak wywoła polecenie).

Wiele przypadków to przypadki narożne , ale należy pamiętać, że which/ typesą często używane w przypadkach narożnych (aby znaleźć odpowiedź na nieoczekiwane zachowanie, takie jak: dlaczego, u licha, to polecenie zachowuje się tak, do którego ja wzywam? ).

Większość systemów, większość powłok Bourne'a: ​​funkcje

Najbardziej oczywistym przypadkiem są funkcje:

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

Powodem jest to, że whichraportuje tylko o plikach wykonywalnych, a czasami o aliasach (choć nie zawsze tych w twojej powłoce), a nie funkcjach.

GNU, którego strona man ma zepsuty (jak zapomnieli zacytować $@) przykład, jak go używać do raportowania funkcji, ale podobnie jak w przypadku aliasów, ponieważ nie implementuje on 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 typu Bourne'a: ​​wbudowane

Inna sprawa jest oczywista builtins lub słowa kluczowe, jak whichjest jakaś komenda nie ma sposobu, aby wiedzieć, które builtins twoja powłoka posiada (i niektóre muszle jak zsh, bashlub kshmoże załadować poleceń wbudowanych 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

(nie dotyczy tego, zshgdzie whichjest wbudowany)

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

Jest tak, ponieważ w większości komercyjnych Uniksów which(podobnie jak w oryginalnej implementacji na 3BSD) jest cshskrypt, który czyta ~/.cshrc. Aliasy, które zgłosi, są zdefiniowane tam niezależnie od aktualnie zdefiniowanych aliasów i niezależnie od używanej powłoki.

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 $pathprzed odczytaniem ~/.cshrci przywracając 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 jako cshskrypt 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 istnieje zdefiniowany alias dla całego systemu, który otacza whichpolecenie GNU .

Wyjście podrobiony dlatego whichczyta wyjście bash„s alias, ale nie wie, jak go analizować i prawidłowo używa heurystyki (jeden alias każdym wierszu, wygląda na pierwszy znaleziono polecenia po |, ;, &...)

Najgorsze w CentOS jest to, że zshma doskonale whichwbudowane polecenie, ale CentOS zdołał go złamać, zastępując go niedziałającym aliasem dla 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

Na Debianie /bin/whichjest /bin/shskryptem. W moim przypadku shbycie dashjest takie samo, kiedy jest bash.

Unset PATHnie ma na celu wyłączenia PATHwyszukiwania, ale oznacza użycie domyślnej ścieżki systemu, która niestety w Debianie nikt się nie zgadza ( dashi bashma /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin, zshma /bin:/usr/bin:/usr/ucb:/usr/local/bin, ksh93ma /bin:/usr/bin, mkshma /usr/bin:/bin( $(getconf PATH)), execvp()(jak w env) ma :/bin:/usr/bin(tak, najpierw szuka w bieżącym katalogu! )).

Dlatego whichdostaje to źle powyżej ponieważ używa dash„s domyślnego PATH, który jest różny od ksh93” s

Nie jest lepiej z GNU, whichktóry raportuje:

which: no which in ((null))

(co ciekawe, /usr/local/bin/whichw moim systemie jest rzeczywiście akangaskrypt, który w rzeczywistości został dostarczony ze skryptem akanga( rcpochodna powłoki, gdzie domyślnie PATHjest /usr/ucb:/usr/bin:/bin:.))

bash, dowolny system:

Ten, do którego odnosi się Chris w 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 hashręcznym wywołaniu :

$ 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 przypadek, w którym whichczasami się typenie udaje:

$ 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 kilkoma pociskami:

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

Z innymi:

$ foo
a/foo

Ani whichnie typemoże wiedzieć z góry, że b/foonie można go wykonać. Niektóre muszle jak bash, kshlub yash, gdy powołując się foorzeczywiście spróbować uruchomić b/fooi zgłosić błąd, podczas gdy inni (jak zsh, ash, csh, Bourne, tcsh) będzie działać a/foona niepowodzenia execve()wywołania systemowego sprawie b/foo.

Stéphane Chazelas
źródło
mkshfaktycznie używa czegoś innego jako domyślnego $PATH: po pierwsze, _PATH_DEFPATHużywana jest stała czasowa kompilacji systemu operacyjnego (najczęściej na BSD), następnie confstr(_CS_PATH, …)używana jest (POSIX), a jeśli obie nie istnieją lub ulegają awarii /bin:/usr/bin:/sbin:/usr/sbin.
mirabilos
1
W twoim pierwszym przykładzie, nawet jeśli lsjest to funkcja, której używa lsz PATH. I whichdobrze jest powiedzieć, który jest używany /usr/bin/ls lub /usr/local/bin/ls. Nie widzę „Dlaczego nie skorzystać z którego” ....
rudimeier
@rudimeier, że which lsda mi /bin/lsniezależnie od tego, czy lsfunkcja zwraca /bin/lslub /opt/gnu/bin/lslub dirlub w ogóle nic. IOW which(to, które implementacje, IMMV) daje coś nieistotnego
Stéphane Chazelas
1
@ StéphaneChazelas. Nie nie nie. I wiem już, że mój lsjest funkcją. Ja wiem , że moja lsfunkcja dzwoni lsz PATH. Teraz whichmówi mi, gdzie jest plik. Widoczny jest tylko jeden przypadek użycia: „Co moja powłoka zrobiłaby z tym poleceniem”. W tym przypadku użycie whichjest nieprawidłowe, popraw. Są jednak inne przypadki użycia, w których (GNU) whichjest dokładnie właściwą rzeczą.
rudimeier
@rudimeter, zależy od whichimplementacji. Niektórzy powiedzą, że to jest alias (jeśli masz skonfigurowany alias lub jeśli ~/.cshrcw domu jest taki alias), inni podadzą ci ścieżkę, ale pod pewnymi warunkami niewłaściwy. sh -c 'command -v ls', chociaż nie doskonały, jest jeszcze bardziej prawdopodobne, że da ci właściwą odpowiedź na te różne wymagania (i jest również standardem).
Stéphane Chazelas
21

Jedną rzeczą, która (z mojego szybkiego przeglądu) wydaje się, że Stephane nie wspomniał, jest to, że whichnie ma pojęcia o tabeli skrótów ścieżki twojej powłoki. Powoduje to, że może zwrócić wynik, który nie jest reprezentatywny dla tego, co jest faktycznie uruchamiane, co czyni go nieefektywnym w debugowaniu.

Chris Down
źródło
6

W duchu UNIX: Niech każdy program robi jedną rzecz dobrze.

Jeśli celem jest odpowiedź: który plik wykonywalny istnieje o tej nazwie?

Dobrym rozwiązaniem jest program wykonywalny dostarczany z systemami Debian. Dostarczone z csh zawierały aliasy, które są źródłem problemów. To, co niektóre powłoki zapewniają jako wbudowane funkcje wewnętrzne, ma inny cel. Użyj tego pliku wykonywalnego lub użyj skryptu podanego na końcu tej odpowiedzi.

Jeśli ten skrypt jest używany, to, co odpowiada, jest czysty, prosty i użyteczny.

Ten cel będzie pasował do pierwszego zdania Twojego pytania:

Szukając ścieżki do pliku wykonywalnego……

Jeśli masz system, który nie posiada plik wykonywalny o nazwie, która (większość systemów Linux masz) można utworzyć w ~/bin/whichprzed /bin/w PATH tak osobiste wykonywalne te systemowe przesłanianie jak ten na dole tego postu:

Ten plik wykonywalny wyświetli (domyślnie) wszystkie pliki wykonywalne znalezione w ŚCIEŻCE. Jeśli wymagana jest tylko pierwsza, opcja -fjest dostępna.


W tym momencie osiągamy inny cel:

co powłoka wykona (po parsowaniu)

Pochodzi z Twojego drugiego zdania:

sprawdzanie, co się stanie, jeśli wpiszesz nazwę polecenia w powłoce uniksowej

Ten drugi przedmiot próbuje znaleźć dobrą odpowiedź na pytanie, na które trudno jest odpowiedzieć. Muszle mają różne poglądy, przypadki narożne i (co najmniej) różne interpretacje. Dodając do tego:

istnieje mnóstwo różnych narzędzi (które, typ, polecenie, skąd, gdzie, whereis, whatis, skrót itp.).

Oczywiście wszystkie próby odpowiadają temu celowi.


Unikaj który?

Często słyszymy o tym, czego należy unikać.

Zastanawiam się: dlaczego miałoby to być powiedziane, jeśli whichdziała dobrze (przynajmniej w Debianie)?

W duchu UNIX: Niech każdy program robi jedną rzecz dobrze.

Zewnętrzny program, whichrobi jedno: Znajdź pierwszy wykonywalnego na ścieżkę, która ma taką samą nazwę jak nazwa polecenia . I robi to dość dobrze.

Nie znam żadnego innego programu ani narzędzia, które odpowiedziałoby na to pytanie w bardziej fundamentalny sposób. Jako taki jest przydatny i może być używany w razie potrzeby.

Najbliższa alternatywa wydaje się być command -pv commandName:, ale będzie to również raportować o wbudowanych i aliasach. Nie ta sama odpowiedź.

Oczywiście whichjest ograniczony, nie odpowiada na wszystkie pytania, żadne narzędzie nie mogłoby tego zrobić (cóż, jeszcze nie ...). Ale przydaje się, gdy jest używany do odpowiedzi na pytanie, na które ma odpowiedzieć (to pytanie powyżej). Podobnie jak edzostało ograniczone, a następnie sedpojawił się (lub vi/ vim). Lub podobny awkbył ograniczony i sprawił, że Perl pojawił się i rozszerzył. Jednak ed, sedi / lub awkmają szczególne przypadki użycia, gdzie vimalbo perlnie najlepsze narzędzia.

Dlaczego?

Prawdopodobnie dlatego, że whichodpowiada tylko na część pytania, które użytkownik powłoki może zadać:

Co jest wykonywane, gdy wpisuję polecenie?


Zewnętrzne które

Który powinien być dostępny (w wielu systemach) jako zewnętrzny plik wykonywalny.
Jedynym pewnym sposobem na wywołanie tego narzędzia zewnętrznego jest użycie env, aby wyjść z powłoki, a następnie wywołać which(co działa we wszystkich powłokach):

 $ env which which
 /usr/bin/which

Lub użyj pełnej ścieżki do which(która może być różna w różnych systemach):

 /usr/bin/which which 

Dlaczego to jest hackpotrzebne? Ponieważ niektóre powłoki (szczególnie zsh) chowają się which:

 $ zsh -c 'which which'
 which: shell built-in command

Będąc narzędziem zewnętrznym (jak env) doskonale wyjaśnia, dlaczego nie zgłasza wewnętrznych informacji powłoki. Podobnie jak aliasy, funkcje, wbudowane, specjalne wbudowane, zmienne powłoki (nieeksportowane) itp .:

 $ env which ls
 /usr/bin/ls
 $ env which ll       # empty output

Puste wyjście ll(wspólny alias dla ll='ls -l') wskazuje, że llnie jest związane z programem wykonywalnym, a przynajmniej, że nie ma pliku wykonywalnego o nazwie llPATH. Zastosowanie llpowinno wywołać coś innego, w tym przypadku alias:

 $ type ll
 ll is aliased to `ls -l'

type i command

Polecenia typei command -vsą wymagane przez POSIX. Należy się spodziewać, że będą działać w większości muszli i robią to, z wyjątkiem csh, tcsh, fish i rc.

Oba polecenia mogą być wykorzystane do zapewnienia innego punktu widzenia, które polecenie zostanie wykonane.

whence, where, whereis, whatis,hash

Wtedy istnieje whence, where, whereis, whatis, hash, i kilka innych. Wszystkie różne odpowiedzi na podobne pytania. Wszystkie działają na różne sposoby w różnych powłokach. Prawdopodobnie whencejest najczęstszy po type. Inne są specjalnymi rozwiązaniami, które odpowiadają na to samo pytanie na różne sposoby.

Czego powinniśmy użyć zamiast tego?

Prawdopodobnie whichnajpierw wiedzieć, czy istnieje plik wykonywalny o nazwie nazwa_polecenia , a następnie typei command, a następnie, jeśli nazwa_polecenia nie zostały jeszcze znalezione: whence, where, whereis, whatis, hashw tej kolejności.


Skrypt powłoki zapewniający whichplik wykonywalny.

#! /bin/sh
set -ef; oldIFS=$IFS; IFS=:

say()( IFS=" "; printf "%s\n" "$*"; )
say "Simplified version of which."
usage(){ say Usage: "$0" [-f] args; }
if [ "$#" -eq 0 ]; then say Missing argument(s); usage; exit 2; fi

firstmatch=0
while getopts f whichopts; do
    case "$whichopts" in
        f) firstmatch=1 ;;
        ?) usage; exit 3 ;;
    esac
done
[ "$OPTIND" -gt 1 ] && shift `expr "$OPTIND" - 1`

allret=0; [ "$#" -eq 0 ] && allret=1
for program in "$@"; do
    ret=1
    for element in $PATH''; do
        case "$program" in
            */*) element="$program"; loop=0;;
            *)   element="${element:-.}/$program"; loop=1;;
        esac
        if [ -f "$element" ] && [ -x "$element" ]; then
            say "$element"
            ret=0
            if [ "$firstmatch" -eq 1 ] || [ "$loop" -eq 0 ]; then break; fi
        fi
    done
    [ "$ret" -eq 1 ] && allret=1
done

IFS="$oldIFS"
exit "$allret"
Isaac
źródło
0

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

Nigdy tego nie słyszałem. Podaj konkretne przykłady. Martwiłbym się o twoją dystrybucję linuksa i zainstalowane pakiety oprogramowania, ponieważ to skąd whichpochodzi!

SLES 11,4 x86-64

w wersji tcsh 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.

whichjest częścią util-linux, standardowego pakietu dystrybuowanego przez Linux Kernel Organization do użytku jako część systemu operacyjnego Linux. Zapewnia 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

moja util-linuxjest wersja 2.19. Informacje o wersji można łatwo znaleźć w wersji 2.13 z dnia 28 sierpnia 2007 r. Nie jestem pewien, jaki był tego cel lub cel, z pewnością nie udzielono odpowiedzi w tej długiej sprawie, która została poddana głosowaniu 331 razy.

Ron
źródło
2
Zauważ, że pytanie nie wspomina o tym, do którego Unixa się odnosi. Linux jest tylko jednym z niewielu.
Kusalananda
2
Jak which -vpokazuje program, jest to GNU (ekstrawaganckie, wspomniane w drugiej odpowiedzi i nie jest w żaden sposób specyficzne dla Linuksa), a nie util-linux, którego AFAIK nigdy nie zawierał which. util-linux 2.19 pochodzi z 2011 roku, GNU, który 2.19 pochodzi z 2008 roku.
Stéphane Chazelas