GNU/Linux >> Znalost Linux >  >> Linux

Proč nepoužít „který“? Co tedy použít?

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…)).

Související:Debian – Jak používat proprietární bezdrátové ovladače během instalace Debianu USB?

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ž do execve systémové volání se nevrací s chybou. Například pokud $PATH obsahuje /foo:/bar a chcete spustit ls , 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 jako ash , pdksh nebo zsh , může také vrátit foo 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říklad mount je někdy vestavěný pro busybox sh ) a například bash 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 po cd 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 …), ale command -v chmod vrátí /opt/ast/bin/chmod i když tato cesta neexistuje.
Související:Dd vs cat — je dd v dnešní době stále aktuální?

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 nebo echo=$commands[echo] nebo echo=${${:-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.


Linux
  1. Jak na to:Co je Git a Github? Jak to mám používat a proč by mě to mělo zajímat?

  2. Proč není CD program?

  3. Linux – proč používáme Su – a nejen Su?

  1. Proč je Bash všude (ve většině, ne-li ve všech distribucích Linuxu)?

  2. Proč používáme su – a nejen su?

  3. Jaké znaky mám nebo nemám používat v uživatelských jménech v systému Linux?

  1. Proč nikdo nepoužívá The True Bourne Shell jako /bin/sh?

  2. Co nedávat na Ssd?

  3. Proč neblokovat ICMP?