Když hledáte cestu ke spustitelnému souboru nebo kontrolujete, co by se stalo, když zadáte název příkazu do unixového shellu, existuje spousta různých utilit (which , type , command , whence , where , whereis , whatis , hash , atd.).
Často slýcháme, že which je třeba se vyhnout. Proč? Co bychom měli použít místo toho?
Přijatá odpověď:
Zde je vše, o čem jste si nikdy nemysleli, že o tom nebudete chtít vědět:
Shrnutí
Chcete-li získat cestu ke spustitelnému souboru ve skriptu shellu podobnému Bourneovi (existuje několik upozornění; viz níže):
ls=$(command -v ls)
Chcete-li zjistit, zda daný příkaz existuje:
if command -v given-command > /dev/null 2>&1; then
echo given-command is available
else
echo given-command is not available
fi
Na výzvu interaktivního shellu podobného Bourneovi:
type ls
which Command je přerušené dědictví z C-Shellu a je lepší ho nechat o samotě v Bourneovských mušlích.
Případy použití
Je rozdíl mezi hledáním těchto informací jako součást skriptu nebo interaktivně na příkazovém řádku shellu.
Typický případ použití na příkazovém řádku je:tento příkaz se chová divně, používám ten správný? Co se přesně stalo, když jsem zadal mycmd ? Mohu se dále podívat na to, co to je?
V takovém případě chcete vědět, co váš shell udělá, když vyvoláte příkaz, aniž byste příkaz skutečně vyvolali.
Ve skriptech shellu to bývá docela jiné. Ve skriptu shellu není žádný důvod, proč byste chtěli vědět, kde nebo co je příkaz, pokud vše, co chcete udělat, je spustit jej. Obecně, to, co chcete vědět, je cesta ke spustitelnému souboru, abyste z něj mohli získat více informací (např. cestu k jinému souboru nebo číst informace z obsahu spustitelného souboru na této cestě).
Interaktivně můžete chtít vědět o všech my-cmd příkazy dostupné v systému, ve skriptech, jen zřídka.
Většina dostupných nástrojů (jak tomu často bývá) byla navržena pro interaktivní použití.
Historie
Nejprve trocha historie.
Rané unixové shelly až do konce 70. let neměly žádné funkce ani aliasy. Pouze tradiční vyhledávání spustitelných souborů v $PATH . csh představil aliasy kolem roku 1978 (ačkoli csh byl poprvé vydán v 2BSD , v květnu 1979) a také zpracování .cshrc aby si uživatelé přizpůsobili shell (každý shell jako csh , zní .cshrc i když není interaktivní jako ve skriptech).
Zatímco Bourne shell byl poprvé vydán v Unixu V7 dříve v roce 1979, podpora funkcí byla přidána až mnohem později (1984 v SVR2), a každopádně nikdy neměl nějaký rc soubor (.profile je konfigurovat vaše prostředí, nikoli shell per se ).
csh se stal mnohem populárnějším než Bourne shell, protože (ačkoli měl strašně horší syntaxi než Bourne shell) přidával spoustu pohodlnějších a příjemnějších funkcí pro interaktivní použití.
V 3BSD (1980), which byl přidán skript csh pro csh uživatelům pomoci identifikovat spustitelný soubor a jde o téměř odlišný skript, který můžete najít jako which na mnoha současných komerčních Unices (jako Solaris, HP/UX, AIX nebo Tru64).
Tento skript čte ~/.cshrc uživatele (stejně jako všechny csh skripty ano, pokud nejsou vyvolány pomocí csh -f ) a vyhledá poskytnuté názvy příkazů v seznamu aliasů a v $path (pole, které csh udržuje na základě $PATH ).
Tady to je:which byl první pro nejpopulárnější shell v té době (a csh byl stále populární až do poloviny 90. let), což je hlavní důvod, proč byl zdokumentován v knihách a stále je široce používán.
Všimněte si, že i pro csh uživatel, ten which csh skript nemusí nutně poskytnout správné informace. Získá aliasy definované v ~/.cshrc , nikoli ty, které jste mohli definovat později na výzvu nebo například pomocí source další csh soubor a (i když by to nebyl dobrý nápad), PATH může být předefinováno v ~/.cshrc .
Spuštění tohoto which příkaz z Bourne shellu by stále vyhledával aliasy definované ve vašem ~/.cshrc , ale pokud žádný nemáte, protože nepoužíváte csh , to by vám pravděpodobně dalo správnou odpověď.
Podobná funkce byla přidána do Bourne shellu až v roce 1984 v SVR2 s type vestavěný příkaz. Skutečnost, že je vestavěný (na rozdíl od externího skriptu), znamená, že může vám poskytne správné informace (do určité míry), protože má přístup k vnitřnostem shellu.
Počáteční type příkaz trpěl podobným problémem jako which skript v tom, že pokud příkaz nebyl nalezen, nevrátil stav ukončení selhání. Také u spustitelných souborů, na rozdíl od which , vypíše něco jako ls is /bin/ls místo pouze /bin/ls což ztěžovalo použití ve skriptech.
Bourne shell Unix verze 8 (nevydán ve volné přírodě) měl svůj type vestavěný přejmenován na whatis a rozšířena také o zprávy o parametrech a definicích tiskových funkcí. Opravil také type problém s nevracením chyby, když se nepodařilo najít jméno.
rc , shell Plan9 (kdysi nástupce Unixu) (a jeho deriváty jako akanga a es ) mají whatis také.
Shell Korn (jehož podmnožina POSIX sh definice je založena na), vyvinutý v polovině 80. let, ale nebyl široce dostupný před rokem 1988, přidal mnoho csh funkce (editor řádků, aliasy…) v horní části Bourne shellu. Přidalo vlastní whence vestavěný (kromě type ), který měl několik možností (-v poskytnout type -jako podrobný výstup a -p hledat pouze spustitelné soubory (ne aliasy/funkce…)).
Shodou okolností nepokoje ohledně autorských práv mezi AT&T a Berkeley, několika svobodným softwarem implementace shellu vyšly koncem 80. let začátkem 90. let. Celý Almquist shell (ash , který má nahradit Bourne shell v BSD), implementace ksh ve veřejné doméně (pdksh ), bash (sponzorováno FSF), zsh vyšel v letech 1989 až 1991.
Ash, ačkoli měl být náhradou za Bourne shell, neměl type zabudovaný až mnohem později (v NetBSD 1.3 a FreeBSD 2.3), ačkoli měl hash -v . OSF/1 /bin/sh měl type vestavěný, který vždy vrátil 0 až do OSF/1 v3.x. bash nepřidali whence ale přidal -p možnost type k vytištění cesty (type -p by bylo jako whence -p ) a -a nahlásit vše odpovídající příkazy. tcsh vytvořil which vestavěný a přidán where příkaz fungující jako bash 's type -a . zsh má je všechny.
fish shell (2005) má type příkaz implementovaný jako funkce.
which Skript csh byl mezitím odstraněn z NetBSD (protože byl zabudován v tcsh a v jiných shellech nebyl příliš užitečný) a funkce byla přidána do whereis (při vyvolání jako which , whereis chová se jako which kromě toho, že vyhledává pouze spustitelné soubory v $PATH ). V OpenBSD a FreeBSD, which byl také změněn na ten napsaný v C, který vyhledává příkazy v $PATH pouze.
Implementace
Existují desítky implementací which příkaz na různých Unicích s různou syntaxí a chováním.
V systému Linux (kromě těch vestavěných v tcsh a zsh ) najdeme několik implementací. Na nedávných systémech Debian je to například jednoduchý skript shellu POSIX, který hledá příkazy v $PATH .
busybox má také which příkaz.
Existuje GNU which která je pravděpodobně nejextravagantnější. Snaží se rozšířit to, co which Skript csh udělal s jinými shelly:můžete mu říct, jaké jsou vaše aliasy a funkce, aby vám mohl poskytnout lepší odpověď (a věřím, že některé distribuce Linuxu kolem toho nastavují nějaké globální aliasy pro bash k tomu).
zsh má několik operátorů pro rozbalení na cestu ke spustitelným souborům:= rozšíření názvu souboru operátor a :c modifikátor rozšíření historie (zde se vztahuje na rozšíření parametrů ):
$ print -r -- =ls
/bin/ls
$ cmd=ls; print -r -- $cmd:c
/bin/ls
zsh , v zsh/parameters modul také vytváří hashovací tabulku příkazů jako commands asociativní pole:
$ print -r -- $commands[ls]
/bin/ls
whatis utilita (kromě té v Unixu V8 Bourne shell nebo Plan 9 rc /es ) ve skutečnosti nesouvisí, protože slouží pouze pro dokumentaci (greps databáze whatis, to je synopse manuálové stránky).
whereis byl také přidán do 3BSD současně s which ačkoli to bylo napsáno v C , nikoli csh a používá se k současnému vyhledávání spustitelného souboru, manuálové stránky a zdroje, ale není založeno na aktuálním prostředí. Takže to opět odpovídá na jinou potřebu.
Nyní, na standardní frontě, POSIX specifikuje command -v a -V příkazy (které byly až do POSIX.2008 volitelné). UNIX určuje type příkaz (bez možnosti). To je vše (where , which , whence nejsou specifikovány v žádné normě).
Do některé verze type a command -v byly volitelné ve specifikaci Linux Standard Base, což vysvětluje, proč například některé staré verze posh (ačkoli na základě pdksh který měl obojí) neměl ani jedno. command -v byl také přidán do některých implementací Bourne shellu (jako na Solaris).
Stav dnes
Současný stav je takový, že type a command -v jsou všudypřítomné ve všech skořápkách podobných Bourneovi (ačkoli, jak poznamenal @jarno, všimněte si upozornění/chyby v bash když není v režimu POSIX nebo někteří potomci shellu Almquist níže v komentářích). tcsh je jediný shell, kde byste chtěli použít which (protože neexistuje žádný type tam a which je vestavěný).
V jiných shellech než tcsh a zsh , which vám může sdělit cestu k danému spustitelnému souboru, pokud v žádné z našich ~/.cshrc není alias nebo funkce se stejným názvem , ~/.bashrc nebo jakýkoli spouštěcí soubor shellu a nedefinujete $PATH ve vašem ~/.cshrc . Pokud pro něj máte definovaný alias nebo funkci, může, ale nemusí vám o tom říct, nebo vám říct špatnou věc.
Pokud chcete vědět o všech příkazech podle zadaného jména, není nic přenosného. Použili byste where v tcsh nebo zsh , type -a v bash nebo zsh , whence -a v ksh93 a jiných shellech můžete použít type v kombinaci s which -a což může fungovat.
Doporučení
Získání názvu cesty ke spustitelnému souboru
Nyní, abychom získali cestu ke spustitelnému souboru ve skriptu, existuje několik upozornění:
ls=$(command -v ls)
by byl standardní způsob, jak to udělat.
Existuje však několik problémů:
- Není možné znát cestu ke spustitelnému souboru bez jeho spuštění. Všechny
type,which,command -v… všichni používají heuristiku ke zjištění cesty. Procházejí smyčkou$PATHkomponenty a najděte první neadresářový soubor, pro který máte oprávnění ke spuštění. Nicméně v závislosti na shellu, pokud jde o provedení příkazu, mnoho z nich (Bourne, AT&T ksh, zsh, ash…) je prostě provede v pořadí$PATHaž doexecvesystémové volání se nevrací s chybou. Například pokud$PATHobsahuje/foo:/bara chcete spustitls, nejprve se pokusí spustit/foo/lsnebo pokud to selže/bar/ls. Nyní spuštění/foo/lsmůže selhat, protože nemáte oprávnění ke spuštění, ale také z mnoha dalších důvodů, například to, že to není platný spustitelný soubor.command -v lsby hlásil/foo/lspokud máte oprávnění ke spuštění pro/foo/ls, ale běžílsmůže ve skutečnosti spustit/bar/lsif/foo/lsnení platný spustitelný soubor. - pokud
fooje vestavěná funkce nebo alias,command -v foovrátífoo. S některými shelly jakoash,pdkshnebozsh, může také vrátitfoopokud$PATHobsahuje prázdný řetězec a je zde spustitelnýfoosoubor v aktuálním adresáři. Existují určité okolnosti, kdy to možná budete muset vzít v úvahu. Mějte například na paměti, že seznam vestavěných prvků se liší podle implementace shellu (napříkladmountje někdy vestavěný pro busyboxsh) a napříkladbashmůže získat funkce z prostředí. - pokud
$PATHobsahuje relativní komponenty cesty (typicky.nebo prázdný řetězec, který oba odkazují na aktuální adresář, ale může to být cokoliv), v závislosti na shellu,command -v cmdnemusí vypsat absolutní cestu. Tedy cestu, kterou získáte při spuštěnícommand -vpocdjiž nebude platný někde jinde. - Neoficiální:s shellem ksh93, pokud
/opt/ast/bin(i když věřím, že přesná cesta se může v různých systémech lišit) je ve vás$PATH, ksh93 zpřístupní několik dalších vestavěných modulů (chmod,cmp,cat…), alecommand -v chmodvrátí/opt/ast/bin/chmodi když tato cesta neexistuje.
Určení, zda příkaz existuje
Chcete-li zjistit, zda daný příkaz standardně existuje, můžete provést:
if command -v given-command > /dev/null 2>&1; then
echo given-command is available
else
echo given-command is not available
fi
Kde byste mohli chtít použít which
(t)csh
V csh a tcsh , nemáte moc na výběr. V tcsh , to je v pořádku jako which je vestavěný. V csh , to bude systém which příkaz, který v několika případech nemusí dělat to, co chcete.
Najít příkazy pouze v některých shellech
Případ, kdy může mít smysl použít which je, pokud chcete znát cestu k příkazu, ignorovat potenciální vestavěné shelly nebo funkce v bash , csh (nikoli tcsh ), dash nebo Bourne shell skripty, tedy shelly, které nemají whence -p (jako ksh nebo zsh ), command -ev (jako yash ), whatis -p (rc , akanga ) nebo vestavěný which (jako tcsh nebo zsh ) na systémech, kde which je k dispozici a není to csh skript.
Pokud jsou tyto podmínky splněny, pak:
echo=$(which echo)
by vám poskytl cestu k prvnímu echo v $PATH (kromě rohových pouzder), bez ohledu na to, zda echo také se stane, že je to shell zabudovaný/alias/funkce nebo ne.
V jiných prostředích byste preferovali:
- zsh :
echo==echoneboecho=$commands[echo]neboecho=${${:-echo}:c} - ksh , zsh :
echo=$(whence -p echo) - yash :
echo=$(command -ev echo) - rc , akanga :
echo=`whatis -p echo`(pozor na cesty s mezerami) - ryby :
set echo (type -fp echo)
Všimněte si, že pokud vše, co chcete udělat, je spustit to echo příkaz, nemusíte získat jeho cestu, můžete to udělat:
env echo this is not echoed by the builtin echo
Například pomocí tcsh , aby se zabránilo vestavěnému which od použití:
set Echo = "`env which echo`"
Když potřebujete externí příkaz
Další případ, kdy můžete chtít použít which je, když skutečně potřebujete externí příkaz. POSIX vyžaduje, aby všechny vestavěné shelly (jako command ) být k dispozici také jako externí příkazy, ale bohužel tomu tak není pro command na mnoha systémech. Například je vzácné najít command příkaz na operačních systémech založených na Linuxu, zatímco většina z nich má which příkaz (i když různé s různými možnostmi a chováním).
Případy, kdy byste mohli chtít externí příkaz, by byly všude tam, kde byste provedli příkaz bez vyvolání shellu POSIX.
system("some command line") , popen() … funkce jazyka C nebo různých jazyků vyvolávají shell pro analýzu tohoto příkazového řádku, takže system("command -v my-cmd") pracovat v nich. Výjimkou by byl perl který optimalizuje shell, pokud nevidí žádný speciální znak shellu (kromě mezery). To platí také pro jeho operátor backtick:
$ 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
Přidání tohoto :; výše si vynutí perl tam vyvolat shell. Pomocí which , nemuseli byste tento trik používat.