Waarom niet “ welke ” gebruiken? Wat moet je dan gebruiken?

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

  • Ik denk dat de meeste argumenten tegen het gebruik van 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 van which om het eerste uitvoerbare bestand van een bepaalde naam te vinden in de $PATH “. De meeste antwoorden en redenen tegen which 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 .).
  • @MattBianco, ja, csh (en which is nog steeds een csh -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 van csh. POSIX-shells-gebruikers hebben command -v.
  • @rudimeier, dan is het antwoord altijd tenzij je shell (t)csh (of je ‘ vindt het niet erg als het niet ‘ je het juiste resultaat geeft), gebruik type of command -v in plaats . Zie de antwoorden voor waarom .
  • @rudimeier, (stat $(which ls) is om verschillende redenen fout (--, ontbrekende aanhalingstekens), niet alleen het gebruik van which). U ‘ d gebruikt stat -- "$(command -v ls)". Dat veronderstelt dat ls 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 je ls invoerde) of je een alias geven zoals gedefinieerd in de configuratie van enkele andere shells …
  • @rudimeier, nogmaals, er zijn een aantal voorwaarden waaronder veel which implementaties je niet eens de ls die zou worden gevonden door opzoeken van $PATH (ongeacht wat ls kan aanroepen in uw shell). sh -c 'command -v ls', of zsh -c 'rpm -q --whatprovides =ls' geven u waarschijnlijk het juiste antwoord. Het punt hier is dat which een verbroken erfenis is van csh.

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 de execve systeemaanroep niet terugkeert met een fout. Als $PATH /foo:/bar bevat en u ls 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, maar ls kan in feite /bar/ls draaien als /foo/ls geen geldig uitvoerbaar bestand is.
  • als foo een ingebouwde functie of alias is, retourneert command -v foo foo. Bij sommige shells zoals ash, pdksh of zsh, kan het ook foo als $PATH de lege string bevat en er “een uitvoerbaar foo -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 busybox sh), en bijvoorbeeld bash 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 je cd 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 …), maar command -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 of echo=$commands[echo] of echo=${${:-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 een csh 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 gebruiken which te doen, de geschiedenis van which, implementaties van which, andere opdrachten om gerelateerde taken uit te voeren of redenen om daadwerkelijk te gebruiken which? Waarom zijn de andere commandos beter ? Wat doen ze anders dan which? Hoe vermijden ze de valkuilen? Dit antwoord besteedt eigenlijk meer woorden aan de problemen met de alternatieven dan de problemen met which.
  • command wordt beschreven door POSIX.
  • @St é phaneChazelas Als ik een nieuw bestand maak door touch /usr/bin/mytestfile en daarna command -v mytestfile, het geeft het pad weer (terwijl which 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 liever command -v / type retourneert een fout) omdat ‘ de opdracht is die het zou proberen uit te voeren wanneer u mytestfile, maar het dash gedrag bevat fouten, alsof er ‘ een niet-uitvoerbaar cmd vóór een uitvoerbaar bestand, command -v retourneert het niet-uitvoerbare bestand terwijl het uitvoeren van cmd het uitvoerbare bestand zou uitvoeren (de verkeerde een is ook gehasht). FreeBSD sh (ook gebaseerd op ash) 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 wordt confstr(_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 g ls van PATH. En which 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 de ls functieaanroepen /bin/ls of /opt/gnu/bin/ls of dir 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 mijn ls functie ls aanroept van PATH. Nu which vertelt me waar het bestand is. Je ziet maar één use case: ” Wat zou mijn shell doen met deze opdracht. ” Voor deze use case which 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 een which hulpprogramma heeft opgenomen. util-linux 2.19 komt uit 2011, GNU waarvan 2.19 uit 2008.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *