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$PATH
komponenty 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í$PATH
až doexecve
systémové volání se nevrací s chybou. Například pokud$PATH
obsahuje/foo:/bar
a chcete spustitls
, nejprve se pokusí spustit/foo/ls
nebo pokud to selže/bar/ls
. Nyní spuštění/foo/ls
můž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 ls
by hlásil/foo/ls
pokud máte oprávnění ke spuštění pro/foo/ls
, ale běžíls
může ve skutečnosti spustit/bar/ls
if/foo/ls
není platný spustitelný soubor. - pokud
foo
je vestavěná funkce nebo alias,command -v foo
vrátífoo
. S některými shelly jakoash
,pdksh
nebozsh
, může také vrátitfoo
pokud$PATH
obsahuje prázdný řetězec a je zde spustitelnýfoo
soubor 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říkladmount
je někdy vestavěný pro busyboxsh
) a napříkladbash
může získat funkce z prostředí. - pokud
$PATH
obsahuje 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 cmd
nemusí vypsat absolutní cestu. Tedy cestu, kterou získáte při spuštěnícommand -v
pocd
již 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 chmod
vrátí/opt/ast/bin/chmod
i 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==echo
neboecho=$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.