Bij het zoeken naar het pad naar een uitvoerbaar bestand of het controleren wat er zou gebeuren als je een commandonaam invoert in een Unix-shell, is er “een overvloed aan verschillende hulpprogrammas ( which
, type
, command
, whence
, where
, whereis
, whatis
, hash
, enz.).
We horen vaak dat which
moet worden vermeden. Waarom? Wat moeten we in plaats daarvan gebruiken?
Opmerkingen
Antwoord
Hier is alles waarvan u nooit had gedacht dat u er nooit iets van zou willen weten:
Samenvatting
Om de padnaam van een uitvoerbaar bestand in een Bourne-achtig shellscript (er zijn een paar kanttekeningen; zie hieronder):
ls=$(command -v ls)
Om erachter te komen of een gegeven commando bestaat:
if command -v given-command > /dev/null 2>&1; then echo given-command is available else echo given-command is not available fi
Bij de prompt van een interactieve Bourne-achtige shell:
type ls
De which
commando is een verbroken erfenis van de C-Shell en kan beter alleen gelaten worden in Bourne-achtige shells.
Use Cases
Daar “een onderscheid tussen het zoeken naar die informatie als onderdeel van een script of interactief bij de shell-prompt.
Bij de shell-prompt is het typische gebruik: dit commando gedraagt zich vreemd, gebruik ik de juiste? Wat gebeurde er precies toen ik mycmd
? Kan ik verder kijken naar wat het is?
In dat geval wil je weten wat je shell doet als je het commando aanroept zonder het commando daadwerkelijk aan te roepen.
In shell-scripts is het meestal heel anders. In een shellscript is er geen reden waarom je zou willen weten waar of wat een commando is als je het alleen maar wilt uitvoeren. Over het algemeen is wat u wilt weten het pad van het uitvoerbare bestand, zodat u er meer informatie uit kunt halen (zoals het pad naar een ander bestand relatief daaraan, of informatie uit de inhoud van het uitvoerbare bestand op dat pad lezen).
Interactief wil je misschien meer weten over alle my-cmd
commandos die beschikbaar zijn op het systeem, in scripts, zelden.
De meeste beschikbare tools (zoals vaak het geval is) zijn ontworpen om interactief te worden gebruikt.
Geschiedenis
Eerst een beetje geschiedenis.
De vroege Unix-shells tot eind jaren 70 hadden geen functies of aliassen. Alleen het traditionele opzoeken van uitvoerbare bestanden in $PATH
. csh
introduceerde aliassen rond 1978 (hoewel csh
voor het eerst vrijgegeven werd in 2BSD
, in mei 1979), en ook de verwerking van een .cshrc
voor gebruikers om de shell aan te passen (elke shell, als csh
, leest .cshrc
zelfs als het niet interactief is zoals in scripts).
Terwijl de Bourne-shell voor het eerst werd uitgebracht in Unix V7 eerder in 1979, werd functieondersteuning slechts veel toegevoegd later (1984 in SVR2), en hoe dan ook, het heeft nooit een rc
bestand gehad (het .profile
is om je omgeving te configureren, niet de shell per se ).
csh
werd veel populairder dan de Bourne-shell (hoewel deze een erg slechtere syntaxis had dan de Bourne shell) voegde het veel handigere en leukere functies toe voor interactief gebruik.
In 3BSD
(1980), een which
csh-script is toegevoegd voor de csh
-gebruikers om een uitvoerbaar bestand te helpen identificeren, en het is een nauwelijks ander script dat u kunt vinden als which
op veel commerciële Unices tegenwoordig (zoals Solaris, HP / UX, AIX of Tru64).
Dat script leest de ~/.cshrc
(zoals alle csh
scripts doen, tenzij aangeroepen met csh -f
), en zoekt de opgegeven commandonaam (en) op in de lijst met aliassen en in $path
(de array die csh
onderhoudt op basis van $PATH
).
Alsjeblieft: which
kwam destijds als eerste voor de meest populaire shell (en csh
was nog steeds populair tot halverwege 90s), wat de belangrijkste reden is waarom het werd gedocumenteerd in boeken en nog steeds veel wordt gebruikt.
Merk op dat zelfs voor een csh
gebruiker, which
csh-script geeft u niet noodzakelijk de juiste informatie. Het haalt de aliassen op die zijn gedefinieerd in ~/.cshrc
, niet degene die je later bij de prompt hebt gedefinieerd of bijvoorbeeld door source
door een andere csh
bestand, en (hoewel dat geen goed idee zou zijn), PATH
kan opnieuw worden gedefinieerd in ~/.cshrc
.
Door dat which
commando vanuit een Bourne-shell te draaien, worden nog steeds aliassen opgezocht die zijn gedefinieerd in uw ~/.cshrc
, maar als je er geen hebt omdat je geen csh
gebruikt, krijg je waarschijnlijk nog steeds het juiste antwoord.
Een vergelijkbare functionaliteit is niet toegevoegd aan de Bourne-shell tot 1984 in SVR2 met het type
ingebouwde commando. Het feit dat het is ingebouwd (in tegenstelling tot een extern script) betekent dat het kan u de juiste informatie (tot op zekere hoogte) geeft aangezien het toegang heeft tot de binnenkant van de shell.
De initiële type
-opdracht leed aan een soortgelijk probleem als het which
-script omdat het geen exitstatus mislukte als het commando is niet gevonden. Ook, voor uitvoerbare bestanden, in tegenstelling tot which
, geeft het iets als ls is /bin/ls
uit in plaats van alleen /bin/ls
waardoor het minder gemakkelijk te gebruiken was in scripts.
Unix versie 8″ s (niet vrijgegeven in het wild) Bourne shell had het “s type
ingebouwd hernoemd naar whatis
. En de Plan9 (de toekomstige opvolger van Unix) shell rc
(en zijn afgeleiden zoals akanga
en es
) hebben ook whatis
.
De Korn-schaal (een subset waarvan de POSIX sh
-definitie is gebaseerd op), ontwikkeld in het midden van de jaren 80 maar niet algemeen verkrijgbaar vóór 1988, heeft veel van de csh
-functies toegevoegd ( line editor, aliassen …) bovenop de Bourne-shell. Het heeft zijn eigen whence
ingebouwd (naast type
) toegevoegd, waarvoor verschillende opties nodig waren (-v
om te voorzien van de type
-achtige uitgebreide uitvoer, en -p
om alleen naar uitvoerbare bestanden te zoeken (geen aliassen / functies …)) .
Toevallig aan de onrust met betrekking tot de auteursrechtkwesties tussen AT & T en Berkeley, kwamen er een paar shell-implementaties van gratis software uit in de late jaren 80, begin jaren 90.De hele Almquist-shell (ash
, ter vervanging van de Bourne-shell in BSDs), de implementatie in het publieke domein van ksh
(pdksh
), bash
(gesponsord door de FSF), zsh
kwam uit tussen 1989 en 1991.
Ash, hoewel bedoeld als vervanging voor de Bourne-shell, had pas veel later een type
ingebouwd (in NetBSD 1.3 en FreeBSD 2.3 ), hoewel het hash -v
had. OSF / 1 /bin/sh
had een type
ingebouwd die altijd retourneerde 0 tot OSF / 1 v3.x. bash
voegde geen whence
toe maar voegde een optie naar type
om het pad af te drukken (type -p
zou zijn als whence -p
) en -a
om alle overeenkomende commandos te rapporteren. tcsh
heeft which
ingebouwd en een where
commando toegevoegd dat zich gedraagt als bash
” s type -a
. zsh
heeft ze allemaal.
De fish
shell (2005) heeft een type
commando geïmplementeerd als een functie.
De which
csh-script is ondertussen verwijderd uit NetBSD (omdat het is ingebouwd in tcsh en niet veel gebruikt in andere shells), en de functionaliteit is toegevoegd aan whereis
(indien aangeroepen als which
, whereis
gedraagt zich als which
behalve dat het alleen uitvoerbare bestanden opzoekt in $PATH
). In OpenBSD en FreeBSD, which
werd ook gewijzigd in een geschreven in C die alleen commandos opzoekt in $PATH
.
Implementaties
Er zijn tientallen implementaties van een which
co mmand op verschillende Unices met verschillende syntaxis en gedrag.
Op Linux (naast de ingebouwde in tcsh
en zsh
) vinden we verschillende implementaties. Op recente Debian-systemen is het bijvoorbeeld “een eenvoudig POSIX-shellscript dat naar commandos zoekt in $PATH
.
busybox
heeft ook een which
commando.
Er is een GNU
which
wat waarschijnlijk de meest extravagante is. Het probeert uit te breiden wat het which
csh-script deed naar andere shells: je kunt het vertellen wat je aliassen en functies zijn, zodat het je een beter antwoord (en ik geloof dat sommige Linux-distributies daar een aantal globale aliassen rond hebben ingesteld voor bash
om dat te doen).
zsh
heeft een aantal operatoren om uit te breiden naar het pad van uitvoerbare bestanden: de =
bestandsnaam uitbreiding operator en de :c
modificatie van geschiedenisuitbreiding (hier toegepast op parameteruitbreiding ):
$ print -r -- =ls /bin/ls $ cmd=ls; print -r -- $cmd:c /bin/ls
zsh
, in het module maakt de opdracht hashtabel ook als de commands
associatieve array:
$ print -r -- $commands[ls] /bin/ls
Het whatis
-hulpprogramma (behalve die in Unix V8 Bourne-shell of Plan 9 rc
/ es
) is niet echt gerelateerd omdat het “alleen voor documentatie is (greps de whatis-database, dat is de synopsis van de manpage”).
whereis
was ook toegevoegd in 3BSD
op hetzelfde moment als which
hoewel het is geschreven in C
, niet csh
en wordt gebruikt om tegelijkertijd het uitvoerbare bestand, de man-pagina en de bron op te zoeken, maar niet op basis van de huidige omgeving. Dus nogmaals, dat beantwoordt aan een andere behoefte.
Nu, op het standaardfront, specificeert POSIX de command -v
en -V
commandos (die voorheen optioneel waren tot POSIX.2008). UNIX specificeert het type
commando (geen optie). Dat is alles (where
, which
, whence
worden in geen enkele standaard gespecificeerd) .
Tot sommige versies waren type
en command -v
optioneel in de Linux Standard Base-specificatie, wat verklaart waarom voor bijvoorbeeld enkele oude versies van posh
(hoewel gebaseerd op pdksh
die beide hadden) hadden geen van beide. command -v
is ook toegevoegd aan sommige Bourne shell-implementaties (zoals op Solaris).
Status vandaag
De huidige status is dat type
en command -v
alomtegenwoordig zijn in alle de Bourne-achtige shells (hoewel, zoals opgemerkt door @jarno, let op de waarschuwing / bug in bash
wanneer niet in POSIX-modus of enkele afstammelingen van de Almquist-shell hieronder in opmerkingen). tcsh
is de enige shell waarin u which
zou willen gebruiken (aangezien er geen type
daar en which
is ingebouwd).
In de shells anders dan tcsh
en zsh
, which
kan je het pad van het opgegeven uitvoerbare bestand vertellen, zolang er geen alias of functie met dezelfde naam in een van onze ~/.cshrc
, ~/.bashrc
of een willekeurig shell-opstartbestand en je definieert $PATH
niet in je ~/.cshrc
. Als u een alias of functie heeft gedefinieerd, kan deze u er al dan niet over vertellen, of u het verkeerde vertellen.
Als u het wilt weten over alle commandos met een bepaalde naam, is er niets draagbaars. U “zou where
gebruiken in tcsh
of zsh
, type -a
in bash
of zsh
, whence -a
in ksh93 en in andere shells , kunt u type
gebruiken in combinatie met which -a
wat mogelijk werkt.
Aanbevelingen
De padnaam naar een uitvoerbaar bestand krijgen
Om de padnaam van een uitvoerbaar bestand in een script te krijgen, zijn er een paar kanttekeningen:
ls=$(command -v ls)
zou de standaard manier zijn om het te doen.
Er zijn echter een paar problemen:
- Het is niet mogelijk om het pad naar het uitvoerbare bestand te kennen zonder het uit te voeren. de
type
,which
,command -v
… gebruiken allemaal heuristieken om het pad te achterhalen Ze doorlopen de$PATH
componenten en vinden het eerste niet-directory-bestand waarvoor je toestemming hebt om uit te voeren. vinden op de shell, als het gaat om het uitvoeren van het commando, zullen velen van hen (Bourne, AT & T ksh, zsh, ash …) ze gewoon uitvoeren in de volgorde van$PATH
totdat deexecve
systeemaanroep niet terugkeert met een fout. Als$PATH
/foo:/bar
bevat en uls
wilt uitvoeren, zullen ze eerst proberen om/foo/ls
uit te voeren of als dat niet lukt/bar/ls
. Nu kan de uitvoering van/foo/ls
mislukken omdat je geen uitvoeringsbevoegdheid hebt, maar ook om vele andere redenen, alsof het geen geldig uitvoerbaar bestand is.command -v ls
zou/foo/ls
rapporteren als u uitvoeringsrechten heeft voor/foo/ls
, maarls
kan in feite/bar/ls
draaien als/foo/ls
geen geldig uitvoerbaar bestand is. - als
foo
een ingebouwde functie of alias is, retourneertcommand -v foo
foo
. Bij sommige shells zoalsash
,pdksh
ofzsh
, kan het ookfoo
als$PATH
de lege string bevat en er “een uitvoerbaarfoo
-bestand in de huidige directory staat. Er zijn enkele omstandigheden waarbij u daar rekening mee moet houden. Houd er bijvoorbeeld rekening mee dat de lijst met ingebouwde onderdelen varieert met de shell-implementatie (mount
wordt bijvoorbeeld soms ingebouwd voor busyboxsh
), en bijvoorbeeldbash
kunnen functies uit de omgeving halen. - if
$PATH
bevat relatieve padcomponenten (typisch.
of de lege tekenreeks die beide verwijzen naar de huidige directory maar kan van alles zijn), afhankelijk van de shell,command -v cmd
geeft mogelijk geen absoluut pad weer. Het pad dat u verkrijgt op het moment dat u zal niet langer geldig zijn nadat jecd
ergens anders hebt geplaatst. - Anekdotisch: met de ksh93 shell, als
/opt/ast/bin
(hoewel dat exacte pad kan variëren op verschillende systemen denk ik) bevindt zich in jou$PATH
, ksh93 zal een paar extra ingebouwde versies beschikbaar stellen (chmod
,cmp
,cat
…), maarcommand -v chmod
retourneert/opt/ast/bin/chmod
zelfs als dat pad niet bestaat.
Bepalen of een commando bestaat
Om erachter te komen of een gegeven commando standaard bestaat, kun je het volgende doen:
if command -v given-command > /dev/null 2>&1; then echo given-command is available else echo given-command is not available fi
Waar men misschien which
(t)csh
zou willen gebruiken In csh
en tcsh
heb je niet veel keus. In tcsh
, dat “prima, want which
is ingebouwd. In csh
is dat het systeem which
-commando, dat in sommige gevallen misschien niet doet wat je wilt.
Vind commandos alleen in sommige shells
Een geval waarin het zinvol zou kunnen zijn om which
te gebruiken is als je het pad van een commando wilt weten, waarbij je potentiële shell ingebouwd of functies in bash
, csh
(niet tcsh
), dash
, of Bourne
shellscripts, dat wil zeggen shells die geen whence -p
hebben (zoals ksh
of zsh
), command -ev
(zoals yash
), whatis -p
(rc
, akanga
) of een ingebouwde which
(zoals tcsh
of zsh
) op systemen waar which
is beschikbaar en is niet de csh
script.
Als aan deze voorwaarden is voldaan, dan:
echo=$(which echo)
zou u het pad van de eerste echo
in $PATH
(behalve in hoekgevallen), ongeacht of echo
ook een shell is ingebouwd / alias / functie of niet.
In andere shells “zou je liever:
- zsh :
echo==echo
ofecho=$commands[echo]
ofecho=${${:-echo}:c}
- ksh , zsh :
echo=$(whence -p echo)
- yash :
echo=$(command -ev echo)
- rc , akanga :
echo=`whatis -p echo`
(pas op voor paden met spaties) - vis :
set echo (type -fp echo)
Houd er rekening mee dat als u alleen maar wilt uitvoeren dat echo
commando, je hoeft het pad niet te achterhalen, je kunt het gewoon doen:
env echo this is not echoed by the builtin echo
Bijvoorbeeld, met tcsh
, om te voorkomen dat de ingebouwde which
wordt gebruikt:
set Echo = "`env which echo`"
Wanneer je een extern commando nodig hebt
Een ander geval waarin je which
wilt gebruiken, is wanneer je eigenlijk nodig hebt een extern commando. POSIX vereist dat alle ingebouwde shell-ins (zoals command
) ook beschikbaar zijn als externe commandos, maar helaas “is dat niet het geval voor command
op veel systemen. Het is bijvoorbeeld zeldzaam om een command
-opdracht te vinden op op Linux gebaseerde besturingssystemen, terwijl de meeste een which
commando (hoewel verschillende met verschillende opties en gedragingen).
Gevallen waarin u een extern commando zou willen, zouden overal zijn waar u een commando zou uitvoeren zonder een POSIX-shell aan te roepen.
De system("some command line")
, popen()
… functies van C of verschillende talen roepen een shell aan om die opdrachtregel te ontleden, dus system("command -v my-cmd")
werken er wel in. Een uitzondering hierop is perl
die de shell optimaliseert als deze geen “speciaal shell-teken” ziet (behalve spatie). Dat geldt ook voor de backtick-operator:
$ 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
De toevoeging van dat :;
hierboven dwingt perl
om een shell aan te roepen daar. Door which
te gebruiken, hoef je die truc niet te gebruiken.
Opmerkingen
- @Joe,
which
is eencsh
script op veel commerciële Unices. De reden is historisch, dat ‘ s waarom ik de geschiedenis heb gegeven, zodat mensen begrijpen waar deze vandaan kwam, waarom mensen eraan gewend raakten en waarom er eigenlijk ‘ s is geen reden om het te gebruiken. En ja, sommige mensen gebruiken (t) csh. Nog niet iedereen gebruikt Linux - Na het lezen van dit bericht heb ik veel context gevonden voor het antwoord, maar niet het antwoord zelf.Waar in dit bericht staat eigenlijk waarom niet je
which
gebruikt, in tegenstelling tot dingen die je misschien probeert te gebruikenwhich
te doen, de geschiedenis vanwhich
, implementaties vanwhich
, andere opdrachten om gerelateerde taken uit te voeren of redenen om daadwerkelijk te gebruikenwhich
? Waarom zijn de andere commandos beter ? Wat doen ze anders danwhich
? Hoe vermijden ze de valkuilen? Dit antwoord besteedt eigenlijk meer woorden aan de problemen met de alternatieven dan de problemen metwhich
. -
command
wordt beschreven door POSIX. - @St é phaneChazelas Als ik een nieuw bestand maak door
touch /usr/bin/mytestfile
en daarnacommand -v mytestfile
, het geeft het pad weer (terwijlwhich mytestfile
dat niet doet). - @jarno, oh ja, jij ‘ opnieuw rechts.
bash
zal genoegen nemen met een niet-uitvoerbaar bestand als het ‘ geen uitvoerbaar bestand kan vinden, dus het ‘ s ” OK ” (hoewel men in de praktijk lievercommand -v
/type
retourneert een fout) omdat ‘ de opdracht is die het zou proberen uit te voeren wanneer umytestfile
, maar hetdash
gedrag bevat fouten, alsof er ‘ een niet-uitvoerbaarcmd
vóór een uitvoerbaar bestand,command -v
retourneert het niet-uitvoerbare bestand terwijl het uitvoeren vancmd
het uitvoerbare bestand zou uitvoeren (de verkeerde een is ook gehasht). FreeBSDsh
(ook gebaseerd opash
) heeft dezelfde bug. zsh, yash, ksh, mksh, bash as sh zijn OK.
Antwoord
De redenen waarom iemand niet willen gebruiken which
zijn al uitgelegd, maar hier zijn een paar voorbeelden op een paar systemen waar which
eigenlijk niet werkt.
Op Bourne-achtige shells “vergelijken we de uitvoer van which
met de uitvoer van type
(type
omdat het een shell is die is ingebouwd, is het bedoeld als de basiswaarheid, aangezien het de shell is die ons vertelt hoe het een commando zou aanroepen).
Veel gevallen zijn corner gevallen, maar houd er rekening mee dat which
/ type
vaak worden gebruikt in hoekgevallen (om het antwoord te vinden tot een onverwacht gedrag als: waarom gedraagt dat commando zich in vredesnaam zo, welke noem ik? ).
De meeste systemen, de meeste Bourne-achtige shells: functies
Het meest voor de hand liggende geval is voor functies:
$ type ls ls is a function ls () { [ -t 1 ] && set -- -F "$@"; command ls "$@" } $ which ls /bin/ls
De reden hiervoor is dat which
alleen rapporteert over uitvoerbare bestanden, en soms over aliassen (hoewel niet altijd die van jouw shell), niet over functies.
De GNU waarvan de man-pagina een defect heeft (omdat ze vergaten $@
te citeren) voorbeeld over hoe het te gebruiken om functies te rapporteren, maar net als voor aliassen, omdat het geen shell-syntaxis-parser implementeert, wordt het gemakkelijk voor de gek gehouden:
$ 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; }
De meeste systemen, de meeste Bourne-achtige shells: ingebouwd
Een ander voor de hand liggend geval zijn ingebouwde ins of trefwoorden, aangezien which
een extern commando is en geen manier heeft om te weten welke ingebouwde elementen uw shell heeft (en sommige shells zoals zsh
, bash
of ksh
kunnen ingebouwde modules dynamisch laden):
$ 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
(dat is “niet van toepassing op zsh
waar which
is ingebouwd)
Solaris 10, AIX 7.1, HP / UX 11i, Tru64 5. 1 en vele anderen:
$ 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
Dat komt omdat op de meeste commerciële Unices which
(zoals in de oorspronkelijke implementatie op 3BSD) is een csh
script dat ~/.cshrc
leest. De aliassen die het zal rapporteren, zijn de aliassen die daar zijn gedefinieerd, ongeacht de aliassen die u momenteel hebt gedefinieerd en ongeacht de shell die u daadwerkelijk gebruikt.
In HP / UX of Tru64:
% echo "setenv PATH /bin:/usr/bin" >> ~/.cshrc % setenv PATH ~/bin:/bin:/usr/bin % ln -s /bin/ls ~/bin/ % which ls /bin/ls
(de Solaris- en AIX-versies hebben dat probleem verholpen door $path
op te slaan voordat de ~/.cshrc
en het herstellen voordat u de opdracht (en) opzoekt)
$ 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
Of:
$ 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
(als het een csh
script is, kun je natuurlijk “niet verwachten dat het werkt met argumenten die spaties bevatten …)
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="
Op dat systeem is er een systeembrede alias gedefinieerd die het GNU which
commando omhult.
De nep-uitvoer is omdat which
de uitvoer leest van bash
“s alias
maar weet niet hoe het correct moet worden geparseerd en gebruikt heuristieken (één alias per regel, zoekt naar het eerste gevonden commando na een |
, ;
, &
…)
Het ergste op CentOS is dat zsh
heeft een prima which
ingebouwd commando, maar CentOS slaagde erin het te breken door het te vervangen door een niet-werkende alias voor GNU which
.
Debian 7.0, ksh93:
(hoewel van toepassing op de meeste systemen met veel shells)
$ unset PATH $ which which /usr/local/bin/which $ type which which is a tracked alias for /bin/which
Op Debian, /bin/which
is een /bin/sh
script. In mijn geval is sh
dash
maar het is hetzelfde wanneer het “s bash
.
Een niet-ingestelde PATH
is niet om PATH
opzoeken uit te schakelen, maar betekent dat het systeem “s standaard PATH waarover helaas op Debian niemand het eens is (dash
en bash
hebben /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
, zsh
heeft /bin:/usr/bin:/usr/ucb:/usr/local/bin
, ksh93
heeft /bin:/usr/bin
, mksh
heeft /usr/bin:/bin
($(getconf PATH)
), execvp()
(zoals in env
) heeft :/bin:/usr/bin
(ja, kijkt eerst in de huidige directory!)) .
Dat is de reden waarom which
het hierboven verkeerd doet, aangezien het “dash
” s standaard PATH
die verschilt van ksh93
“s
Het is nee t beter met GNU which
die rapporteert:
which: no which in ((null))
(interessant genoeg is er inderdaad een /usr/local/bin/which
op mijn systeem dat eigenlijk een akanga
script is dat is geleverd met akanga
(een rc
shell-afgeleide waarbij de standaard PATH
/usr/ucb:/usr/bin:/bin:.
))
bash is, elk systeem:
Degene Chris verwijst naar in zijn antwoord :
$ 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
Ook na het handmatig aanroepen van 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)
Nu een geval waarin which
en soms type
mislukken:
$ 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
Nu, met enkele shells:
$ foo bash: ./b/foo: /: bad interpreter: Permission denied
Met anderen:
$ foo a/foo
Noch which
noch type
kan van tevoren weten dat b/foo
niet kan b e uitgevoerd. Sommige shells zoals bash
, ksh
of yash
, wanneer zal inderdaad proberen b/foo
uit te voeren en een fout te rapporteren, terwijl andere (zoals zsh
, ash
, csh
, Bourne
, tcsh
) wordt uitgevoerd a/foo
na het mislukken van de execve()
systeemaanroep op b/foo
.
Opmerkingen
-
mksh
gebruikt eigenlijk iets anders voor de standaard$PATH
: first , wordt de compilatietijdconstante_PATH_DEFPATH
van het besturingssysteem gebruikt (meestal op de BSDs), vervolgens wordtconfstr(_CS_PATH, …)
gebruikt (POSIX), en als beide niet bestaan of mislukken, wordt/bin:/usr/bin:/sbin:/usr/sbin
gebruikt. - In uw eerste voorbeeld, zelfs als
ls
is een functie die het gebruikt gls
van PATH. Enwhich
is prima om u te vertellen welke wordt gebruikt/usr/bin/ls
of/usr/local/bin/ls
. Ik ‘ zie ” Waarom gebruik je die niet ” …. - @rudimeier, Dat
which ls
geeft me/bin/ls
ongeacht of dels
functieaanroepen/bin/ls
of/opt/gnu/bin/ls
ofdir
of helemaal niets. IOW,which
(dat wat implementaties, IMMV) geeft iets irrelevants - @St é phaneChazelas. Nee nee nee. Ik weet al dat mijn
ls
een functie is. Ik weet dat mijnls
functiels
aanroept vanPATH
. Nuwhich
vertelt me waar het bestand is. Je ziet maar één use case: ” Wat zou mijn shell doen met deze opdracht. ” Voor deze use casewhich
is fout, correct.Maar er zijn andere gevallen waarin (GNU)which
precies het juiste is. - @rudimeter, hangt af van de
which
implementatie. Sommigen zullen je vertellen dat ‘ een alias is (als je een alias hebt geconfigureerd of als er een~/.cshrc
in je huis is die zon alias), sommige zullen je een pad geven, maar onder bepaalde omstandigheden de verkeerde.sh -c 'command -v ls'
, hoewel niet perfect, geeft het nog steeds waarschijnlijker het juiste antwoord op die andere vereiste (en is ook standaard).
Antwoord
Een ding dat (op basis van mijn snelle overzicht) lijkt het erop dat Stephane “niet zei dat which
heeft geen idee over de hashtabel van je shell-pad. Dit heeft tot gevolg dat het een resultaat kan retourneren dat niet representatief is voor wat er werkelijk wordt uitgevoerd, waardoor het niet effectief is bij het debuggen.
Answer
Ik krimp meestal ineen als deze vraag wordt aanbevolen aan nietsvermoedende gebruikers, omdat ongegrond bashen op which
voor niemand nuttig is.
Als which
werkt goed en geeft het juiste antwoord op een taak, volgens de Unix-moto: doe één ding, doe het goed , waarom zou which
worden verbannen?
De vraag zou dan moeten zijn, wat werkt goed en doet een specifiek werk goed?
Ten eerste, het externe hulpprogramma op / bin / which in Debian is een shellscript waarvan het doel alleen is om uitvoerbare bestanden met de opgegeven naam op het pad te vermelden. Ik denk dat which
het beoogde doel correct doet. Het laadt geen aliassen, geen functies, niets uit de shell, geeft alleen de eerste (of alle) uitvoerbare bestanden van de opgegeven naam op het PATH weer. De betekenis van het vinden van een bestand met dezelfde naam als opgegeven, is iets dat de gebruiker door haar (hem) zou moeten uitzoeken self.
Ja, andere which
implementaties kunnen (en meestal) specifieke problemen hebben.
Antwoord
We horen vaak wat moet worden vermeden. Waarom? Wat moeten we in plaats daarvan gebruiken?
Ik heb dat nog nooit gehoord. Geef specifieke voorbeelden. Ik zou me zorgen maken over je Linux-distributie en geïnstalleerde softwarepakketten, want dat is waar which
vandaan komt!
SLES 11.4 x86-64
in tcsh-versie 6.18.01:
> which which which: shell built-in command.
in bash-versie 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
is onderdeel van util-linux een standaardpakket gedistribueerd door de Linux Kernel Organization voor gebruik als onderdeel van het Linux-besturingssysteem. Het biedt ook deze andere bestanden
/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
mijn util-linux
is versie 2.19. Release-opmerkingen zijn gemakkelijk terug te vinden tot v2.13 gedateerd (28-aug-2007). Ik weet niet zeker wat het punt of het doel hiervan was, het werd zeker niet beantwoord in dat lange bericht dat 331 keer werd gestemd.
Opmerkingen
- Merk op hoe de vraag vermeldt niet waar Unix naar verwijst. Linux is slechts een van de weinige.
- Zoals je
which -v
laat zien, dat ‘ s GNU die (de extravagante één genoemd in het andere antwoord en is op geen enkele manier specifiek voor Linux), niet util-linux, waarvoor AFAIK nooit eenwhich
hulpprogramma heeft opgenomen. util-linux 2.19 komt uit 2011, GNU waarvan 2.19 uit 2008.
which
uitgaan van een interactieve shell-context. Deze vraag is getagd / draagbaarheid. Dus ik interpreteer de vraag in deze context als ” wat te gebruiken in plaats vanwhich
om het eerste uitvoerbare bestand van een bepaalde naam te vinden in de$PATH
“. De meeste antwoorden en redenen tegenwhich
omgaan met aliassen, ingebouwde ins en functies, die in de meeste echte draagbare shell-scripts alleen van academisch belang zijn. Lokaal gedefinieerde aliassen worden niet ‘ geërfd bij het uitvoeren van een shellscript (tenzij u het broncode met.
).csh
(enwhich
is nog steeds eencsh
-script voor de meeste commerciële Unices) leest wel~/.cshrc
wanneer niet-interactief. Dat ‘ is waarom je ‘ opmerkt dat csh-scripts meestal beginnen met#! /bin/csh -f
.which
niet omdat het beoogt u de aliassen te geven, omdat het ‘ is bedoeld als hulpmiddel voor (interactieve) gebruikers vancsh
. POSIX-shells-gebruikers hebbencommand -v
.(t)csh
(of je ‘ vindt het niet erg als het niet ‘ je het juiste resultaat geeft), gebruiktype
ofcommand -v
in plaats . Zie de antwoorden voor waarom .stat $(which ls)
is om verschillende redenen fout (--
, ontbrekende aanhalingstekens), niet alleen het gebruik vanwhich
). U ‘ d gebruiktstat -- "$(command -v ls)"
. Dat veronderstelt datls
inderdaad een commando is dat in het bestandssysteem wordt aangetroffen (niet een ingebouwde shell of functie van alias).which
kan je het verkeerde pad geven (niet het pad dat je shell zou uitvoeren als jels
invoerde) of je een alias geven zoals gedefinieerd in de configuratie van enkele andere shells …which
implementaties je niet eens dels
die zou worden gevonden door opzoeken van$PATH
(ongeacht watls
kan aanroepen in uw shell).sh -c 'command -v ls'
, ofzsh -c 'rpm -q --whatprovides =ls'
geven u waarschijnlijk het juiste antwoord. Het punt hier is datwhich
een verbroken erfenis is vancsh
.