Varför inte använda “ vilken ”? Vad ska jag använda då?

När du letar efter sökvägen till en körbar eller kontrollerar vad som skulle hända om du anger ett kommandonamn i ett Unix-skal, finns det en mängd olika verktyg ( which, type, command, whence, where, whereis, whatis, hash, etc).

Vi hör ofta att which bör undvikas. Varför? Vad ska vi använda istället?

Kommentarer

  • Jag tror att de flesta argumenten mot att använda which antar ett interaktivt skalkontext. Denna fråga är taggad / portabilitet. Så jag tolkar frågan i detta sammanhang som ” vad man ska använda istället för which för att hitta den första körningen av ett givet namn i $PATH ”. De flesta svar och skäl mot which hanterar alias, inbyggda funktioner och funktioner som i de flesta bärbara skalskript i verkligheten bara är av akademiskt intresse. Lokalt definierade alias är inte ’ t ärvda när du kör ett skalskript (såvida du inte köper det med .).
  • @MattBianco, ja, csh (och which är fortfarande ett csh -skript på de flesta kommersiella Unices) läser ~/.cshrc när de inte är interaktiva. Det är ’ varför du ’ märker att csh-skript brukar börja med #! /bin/csh -f. which beror inte på att det syftar att ge dig aliaserna, eftersom det ’ är avsett som ett verktyg för (interaktiva) användare av csh. POSIX-skalanvändare har command -v.
  • @rudimeier, då är svaret alltid om inte ditt skal är (t)csh (eller tänker du ’ t om det inte ’ t ger dig rätt resultat), använd type eller command -v istället . Se svaren för varför .
  • @rudimeier, (stat $(which ls) är fel av flera skäl (saknas --, saknade citat), inte bara användningen av which). Du ’ använder stat -- "$(command -v ls)". Det förutsätter att ls verkligen är ett kommando som finns i filsystemet (inte en inbyggd del av ditt skal eller en aliasfunktion). which kan ge dig fel väg (inte den sökväg som ditt skal skulle utföra om du angav ls) eller ge dig ett alias som definierat i konfigurationen av några andra skal …
  • @rudimeier, igen, det finns ett antal villkor under vilka många which implementeringar inte skulle ge dig ens ls som skulle hittas genom en uppslagning av $PATH (oavsett vad ls kan åberopa i ditt skal). sh -c 'command -v ls' eller zsh -c 'rpm -q --whatprovides =ls' är mer benägna att ge dig rätt svar. Poängen här är att which är ett trasigt arv från csh.

Svar

Här är allt du aldrig trodde att du aldrig skulle vilja veta om det:

Sammanfattning

För att få sökväg för en körbar i ett Bourne-liknande skalskript (det finns några försiktighetsåtgärder; se nedan):

ls=$(command -v ls) 

För att ta reda på om ett givet kommando finns:

if command -v given-command > /dev/null 2>&1; then echo given-command is available else echo given-command is not available fi 

Vid uppmaningen till ett interaktivt Bourne-liknande skal:

type ls 

The which -kommandot är ett trasigt arv från C-Shell och är bättre lämnat ensam i Bourne-liknande skal.

Använd fall

Där ”en åtskillnad mellan att leta efter den informationen som en del av ett skript eller interaktivt vid shellprompten.

Vid shellprompten är det typiska användningsfallet: detta kommando beter sig konstigt, använder jag rätt? Vad hände exakt när jag skrev mycmd? Kan jag titta vidare på vad det är?

I så fall vill du veta vad ditt skal gör när du åberopar kommandot utan att faktiskt åberopa kommandot.

I skalskript tenderar det att vara helt annorlunda. I ett skalskript finns det ingen anledning till varför du vill veta var eller vad ett kommando är om allt du vill göra är att köra det. I allmänhet är det du vill veta sökvägen till den körbara filen, så att du kan få mer information ur den (som sökvägen till en annan fil i förhållande till den, eller läsa information från innehållet i den körbara filen på den vägen).

Interaktivt kanske du vill veta om alla kommandona my-cmd som är tillgängliga i systemet, i skript, sällan så.

De flesta tillgängliga verktyg (som ofta är fallet) har utformats för att användas interaktivt.

Historia

Lite historik först.

De tidiga Unix-skalen fram till slutet av 70-talet hade inga funktioner eller alias. Endast den traditionella sökningen av körbara filer i $PATH. csh introducerade alias omkring 1978 (även om csh först släpptes i 2BSD, i maj 1979), och också behandlingen av en .cshrc för att användare ska kunna anpassa skalet (varje skal, som csh , läser .cshrc även när det inte är interaktivt som i skript).

Medan Bourne-skalet först släpptes i Unix V7 tidigare 1979, tillsattes funktionsstöd bara mycket senare (1984 i SVR2), och ändå hade den aldrig någon rc -fil (.profile är att konfigurera din miljö, inte skalet per se ).

csh blev mycket mer populär än Bourne-skalet som (även om det hade en väldigt sämre syntax än Bourne shell) det lade till mycket mer praktiska och trevliga funktioner för interaktiv användning.

I 3BSD (1980), en which csh-skript har lagts till för csh -användarna för att hjälpa till att identifiera en körbar, och det är knappast ett annat skript som du kan hitta som which på många kommersiella enheter idag (som Solaris, HP / UX, AIX eller Tru64).

Det manuset läser användaren” s ~/.cshrc (som alla csh -skript gör om de inte anropas med csh -f) och letar upp det angivna kommandonamnet i listan med alias och i $path (den matris som csh upprätthåller baserat på $PATH).

Så här: which kom först efter det mest populära skalet vid den tiden (och csh var fortfarande populärt fram till mitten av 90-talet), vilket är den främsta anledningen till att den dokumenterades i böcker och fortfarande används i stor utsträckning.

Observera att, även för en csh -användare, att which csh-skript ger dig inte nödvändigtvis rätt information. Det får de alias som definieras i ~/.cshrc, inte de du kanske har definierat senare vid prompten eller till exempel genom att source inge en annan csh fil, och (även om det inte skulle vara en bra idé), PATH kan omdefinieras i ~/.cshrc.

Att köra det which -kommandot från ett Bourne-skal skulle fortfarande slå upp alias definierade i ditt ~/.cshrc om du inte har en eftersom du inte använder csh, skulle det ändå förmodligen ge dig rätt svar.

En liknande funktionalitet lades inte till Bourne-skalet fram till 1984 i SVR2 med type inbyggt kommando. Det faktum att det är inbyggt (i motsats till ett externt skript) betyder att det kan ge dig rätt information (till viss del) eftersom det har tillgång till skalets inre.

Det ursprungliga type -kommandot led av ett liknande problem som which -skriptet genom att det inte returnerade en status för felutgång om kommandot hittades inte. För körbara filer, i motsats till which, skickar det ut något som ls is /bin/ls istället för bara /bin/ls vilket gjorde det mindre enkelt att använda i skript.

Unix version 8″ s (släpptes inte i naturen) Bourne-skal hade det ”s type inbyggt namn till whatis. Och Plan9 (en gång-efter-efterträdare för Unix) skal rc (och dess derivat som akanga och es) har också whatis.

Korn-skalet (en delmängd av vilken POSIX sh -definitionen baseras på), utvecklad i mitten av 80-talet men inte allmänt tillgänglig före 1988, lade till många av csh -funktionerna ( linjeditor, alias …) ovanpå Bourne-skalet. Den lade till sin egen whence inbyggd (förutom type) som tog flera alternativ (-v för att tillhandahålla type -liknande detaljerad utdata, och -p för att bara leta efter körbara filer (inte alias / funktioner …)) .

Samtidigt med oro med avseende på upphovsrättsfrågor mellan AT & T och Berkeley, kom några fri programvara skalimplementeringar ut i slutet av 80-talet början av 90-talet.Hela Almquist-skalet (ash, för att ersätta Bourne-skalet i BSD), det offentliga området för ksh (pdksh), bash (sponsrad av FSF), zsh kom ut mellan 1989 och 1991.

Ash, även om den skulle vara en ersättning för Bourne-skalet, hade inte en type inbyggd förrän långt senare (i NetBSD 1.3 och FreeBSD 2.3 ), även om det hade hash -v. OSF / 1 /bin/sh hade en type inbyggd returnerade 0 upp till OSF / 1 v3.x. bash lade inte till en whence men lade till en -p alternativ till type för att skriva ut sökvägen (type -p skulle vara som whence -p) och -a för att rapportera alla matchande kommandon. tcsh gjorde which inbyggt och lade till ett where -kommando som fungerar som bash” s type -a. zsh har alla.

fish shell (2005) har ett type -kommando implementerat som en funktion.

which csh-skript togs under tiden bort från NetBSD (eftersom det var inbyggt i tcsh och inte användes mycket i andra skal), och funktionaliteten lades till whereis (när den anropas som which, whereis beter sig som which förutom att den bara letar upp körbara filer i $PATH). I OpenBSD och FreeBSD ändrades which till en skriven i C som bara letar upp kommandon i $PATH .

Implementeringar

Det finns dussintals implementeringar av en which co mm och på olika enheter med olika syntax och beteende.

På Linux (bredvid de inbyggda i tcsh och zsh ) vi hittar flera implementeringar. På nyligen gjorda Debian-system är det till exempel ett enkelt POSIX-skalskript som letar efter kommandon i $PATH.

busybox har också ett which -kommando.

Det finns ett GNU which som förmodligen är den mest extravaganta. Den försöker utvidga vad which csh-skriptet gjorde till andra skal: du kan berätta vad dina alias och funktioner är så att det kan ge du har ett bättre svar (och jag tror att vissa Linux-distributioner anger några globala alias runt det för bash för att göra det).

zsh har ett par operatörer för att expandera till sökvägen till körbara filer: operatorn = filnamnsexpansion och :c expansionsmodifierare för historia (här tillämpas på parameterutvidgning ):

$ print -r -- =ls /bin/ls $ cmd=ls; print -r -- $cmd:c /bin/ls 

zsh, i -modulen gör också kommandot hash-tabellen som commands associerande array:

$ print -r -- $commands[ls] /bin/ls 

Verktyget whatis (förutom det i Unix V8 Bourne-skal eller Plan 9 rc / es) är inte riktigt relaterat eftersom det bara är för dokumentation (greps whatis-databasen, det vill säga mansidans synopsis ”).

whereis var också läggs till i 3BSD samtidigt som which även om det skrevs i C, inte csh och används för att slå upp samtidigt, körbar, man-sida och källa men inte baserat på den aktuella miljön. Så igen svarar det på ett annat behov.

Nu, på standardfronten, anger POSIX command -v och -V kommandon (som brukade vara valfria fram till POSIX.2008). UNIX anger kommandot type (inget alternativ). Att ”s alla (where, which, whence anges inte i någon standard) .

Upp till någon version var type och command -v valfria i Linux Standard Base-specifikationen vilket förklarar varför för några gamla versioner av posh (men baserade på pdksh som hade båda) hade inte heller någon. command -v lades också till i vissa Bourne-skalimplementeringar (som på Solaris).

Status idag

Nuvarande status är att type och command -v är allmänt förekommande de Bourne-liknande skalen (dock, som noterat av @jarno, notera varningen / buggen i bash när du inte är i POSIX-läge eller några ättlingar till Almquist-skalet nedan i kommentarer). tcsh är det enda skalet där du vill använda which (eftersom det inte finns någon type där och which är inbyggt).

I andra skal än tcsh och zsh, which kan berätta vägen för den givna körbara så länge det inte finns något alias eller funktion med samma namn i någon av våra ~/.cshrc, ~/.bashrc eller någon shell-startfil och du definierar inte $PATH i din ~/.cshrc. Om du har ett alias eller en funktion definierad för det, kan det berätta om det eller inte, eller berätta fel sak.

Om du vill veta om alla kommandon med ett givet namn finns inget bärbart. Du skulle använda where i tcsh eller zsh, type -a i bash eller zsh, whence -a i ksh93 och i andra skal kan du använda type i kombination med which -a vilket kan fungera.

Rekommendationer

Få sökväg till en körbar

Nu, för att få sökväg till en körbar i ett skript, finns det några försiktighetsåtgärder:

ls=$(command -v ls) 

skulle vara det vanliga sättet att göra det.

Det finns dock några problem:

  • Det går inte att känna till den körbara sökvägen utan att köra den. type, which, command -v … alla använder heuristik för att ta reda på vägen De går igenom $PATH -komponenterna och hittar den första icke-katalogfilen som du har behörighet för. nding på skalet, när det gäller att utföra kommandot, kommer många av dem (Bourne, AT & T ksh, zsh, ash …) att exekvera dem i storleksordningen $PATH tills execve systemanropet inte returnerar med ett fel. Till exempel om $PATH innehåller /foo:/bar och du vill köra ls, försöker de först att köra /foo/ls eller om det misslyckas /bar/ls. Nu kan körningen av /foo/ls misslyckas eftersom du inte har exekveringsbehörighet men också av många andra skäl, eftersom det inte är en giltig körbar. command -v ls skulle rapportera /foo/ls om du har exekveringsbehörighet för /foo/ls, men kör ls kanske kör /bar/ls om /foo/ls inte är en giltig körbar.
  • om foo är en inbyggd funktion eller alias, returnerar command -v foo foo. Med vissa skal som ash, pdksh eller zsh kan det också returnera foo om $PATH innehåller den tomma strängen och det finns en körbar foo -fil i den aktuella katalogen. Det finns vissa omständigheter där du kan behöva ta hänsyn till det. Tänk på att listan med inbyggda enheter varierar med skalimplementeringen (till exempel mount är ibland inbyggd för upptagenbox sh), och till exempel bash kan få funktioner från miljön.
  • om $PATH innehåller relativa sökvägskomponenter (typiskt . eller den tomma strängen som båda hänvisar till den aktuella katalogen men kan vara vad som helst), beroende på skalet, command -v cmd kanske inte matar ut en absolut sökväg. Så sökvägen du får när du kör är inte längre giltig när du cd någon annanstans.
  • Anekdotisk: med ksh93-skalet, om /opt/ast/bin (även om den exakta sökvägen kan variera på olika system tror jag) finns i dig $PATH, ksh93 kommer att göra några extra inbyggda tillgängliga (chmod, cmp, cat …), men command -v chmod returnerar /opt/ast/bin/chmod även om den sökvägen inte finns.

Bestäm om ett kommando existerar

För att ta reda på om ett visst kommando existerar kan du göra:

if command -v given-command > /dev/null 2>&1; then echo given-command is available else echo given-command is not available fi 

Där man kanske vill använda which

(t)csh

I csh och tcsh har du inte mycket val. I tcsh ”s bra som which är inbyggd. I csh kommer det att vara kommandot which, vilket kanske inte gör vad du vill i några få fall.

Hitta kommandon endast i vissa skal

Ett fall där det kan vara vettigt att använda which är om du vill veta sökvägen för ett kommando och ignorera potentialen skalinbyggda funktioner eller funktioner i bash, csh (inte tcsh), dash eller Bourne skalskript, det vill säga skal som inte har whence -p (som ksh eller zsh), command -ev (som yash ), whatis -p (rc, akanga) eller en inbyggd which (som tcsh eller zsh) på system där which tillgänglig och är inte csh script.

Om dessa villkor är uppfyllda, skulle:

echo=$(which echo) 

ge dig vägen till det första echo i $PATH (utom i hörnfall), oavsett om echo också råkar vara ett skal inbyggt / alias / funktion eller inte.

I andra skal skulle du föredra:

  • zsh : echo==echo eller echo=$commands[echo] eller echo=${${:-echo}:c}
  • ksh , zsh : echo=$(whence -p echo)
  • yash : echo=$(command -ev echo)
  • rc , akanga : echo=`whatis -p echo` (se upp för banor med mellanslag)
  • fisk : set echo (type -fp echo)

Observera att om allt du vill göra är kör att echo -kommandot, du behöver inte få sin väg, du kan bara göra:

env echo this is not echoed by the builtin echo 

Till exempel med tcsh, för att förhindra att den inbyggda which används:

set Echo = "`env which echo`" 

När du behöver ett externt kommando

Ett annat fall där du kanske vill använda which är när du faktiskt behöver ett externt kommando. POSIX kräver att alla inbyggda skal (som command) också är tillgängliga som externa kommandon, men tyvärr är det inte fallet för command på många system. Det är till exempel sällsynt att hitta ett command -kommando på Linux-baserade operativsystem medan de flesta av dem har ett which kommando (men olika med olika alternativ och beteenden).

Fall där du kanske vill ha ett externt kommando skulle vara var du än kör ett kommando utan att anropa ett POSIX-skal.

system("some command line"), popen() … funktioner på C eller olika språk åberopar ett skal för att analysera kommandoraden, så system("command -v my-cmd") fungerar i dem. Ett undantag från detta skulle vara perl som optimerar ut skalet om det inte ser något skal-specialtecken (annat än mellanslag). Det gäller även dess backtick-operatör:

$ 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 

Tillägget av det :; ovan tvingar perl att anropa ett skal där. Genom att använda which, skulle du inte behöva använda det tricket.

Kommentarer

  • @Joe, which är ett csh -skript på många kommersiella enheter. Anledningen är historisk, att ’ varför jag gav historien, så att folk förstår varifrån den kom, varför folk vände sig vid att använda den och varför det faktiskt ’ är ingen anledning att du ska använda den. Och ja, vissa använder (t) csh. Inte alla använder Linux ännu
  • Efter att ha läst det här inlägget har jag hittat mycket sammanhang för svaret, men inte själva svaret.Var i det här inlägget står det faktiskt varför inte att använda which, i motsats till saker du kanske försöker använda which att göra, which, implementeringar av which, andra kommandon för att göra relaterade uppgifter eller skäl att faktiskt använda which? Varför är de andra kommandona bättre ? Vad gör de annorlunda än which? Hur undviker de fallgroparna? Detta svar spenderar faktiskt fler ord på problemen med alternativen än problemen med which.
  • command beskrivs av POSIX.
  • @St é phaneChazelas Om jag skapar en ny fil med touch /usr/bin/mytestfile och kör sedan command -v mytestfile, det kommer att ge sökvägen (medan which mytestfile inte).
  • @jarno, åh ja, du ’ är rätt. bash bosätter sig på en icke-körbar fil om den ’ inte hittar en körbar fil, så att den ’ s ” OK ” (men i praktiken skulle man hellre command -v / type returnerar ett fel) som att ’ är kommandot som det skulle försöka utföra när du kör mytestfile, men dash beteende är buggy, som om det ’ är en icke-körbar cmd före en körbar en command -v returnerar den icke-körbara medan exekvering av cmd skulle köra den körbara (fel man är också hashad). FreeBSD sh (även baserat på ash) har samma fel. zsh, yash, ksh, mksh, bash som sh är OK.

Svar

Anledningarna till varför man kan vill inte använda which har redan förklarats, men här är några exempel på några system där which faktiskt misslyckas.

På Bourne-liknande skal jämför vi utdata från which med utdata från type (type eftersom det är ett inbyggt skal är det meningen att det ska vara markens sanning, eftersom det är skalet som berättar för oss hur det skulle åberopa ett kommando.

Många fall är hörn fall, men kom ihåg att which / type ofta används i hörnfall (för att hitta svaret till ett oväntat beteende som: varför i helvete fungerar det kommandot så, vilken ringer jag? ).

De flesta system, de flesta Bourne-liknande skalen: funktioner

Det mest uppenbara fallet är för funktioner:

$ type ls ls is a function ls () { [ -t 1 ] && set -- -F "$@"; command ls "$@" } $ which ls /bin/ls 

Anledningen är att which bara rapporterar om körbara filer, och ibland om alias (men inte alltid de för ditt skal), inte om funktioner.

GNU: s man-sida har ett brutet (eftersom de glömde att citera $@) exempel på hur man använder det för att rapportera funktioner också, men precis som för eftersom det inte implementerar en skal-syntax-parser, luras det lätt:

$ 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 flesta system, de flesta Bourne-liknande skal: inbyggda

Ett annat uppenbart fall är inbyggda eller nyckelord, eftersom which att vara ett externt kommando har inget sätt att veta vilka inbyggda skal som du har (och vissa skal som zsh, bash eller ksh kan ladda inbyggda dynamiskt):

$ 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 

(det gäller inte zsh där which är inbyggt)

Solaris 10, AIX 7.1, HP / UX 11i, Tru64 5. 1 och många andra:

$ 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 

Det beror på att de flesta kommersiella enheter which (som i den ursprungliga implementeringen på 3BSD) är ett csh -skript som läser ~/.cshrc. De alias som den kommer att rapportera är de som definierats där oavsett de alias som du för närvarande har definierat och oavsett skalet du faktiskt använder.

I HP / UX eller Tru64:

% echo "setenv PATH /bin:/usr/bin" >> ~/.cshrc % setenv PATH ~/bin:/bin:/usr/bin % ln -s /bin/ls ~/bin/ % which ls /bin/ls 

(Solaris- och AIX-versionerna har åtgärdat problemet genom att spara $path innan du läser ~/.cshrc och återställa det innan du letar upp kommandot (erna)

$ 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 

Eller:

$ 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 

(naturligtvis, som ett csh -skript kan du inte förvänta dig att det fungerar med argument som innehåller mellanslag …)

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=" 

På det systemet finns det ett alias definierat systemomfattande som omsluter kommandot GNU which.

Den falska utgången beror på att which läser utdata från bash ”s alias men vet inte hur man analyserar det ordentligt och använder heuristik (ett alias per rad, letar efter det första hittade kommandot efter ett |, ;, & …)

Det värsta på CentOS är att zsh har ett helt fint which inbyggt kommando men CentOS lyckades bryta det genom att ersätta det med ett icke-fungerande alias till GNU which.

Debian 7.0, ksh93:

(men gäller de flesta system med många skal)

$ unset PATH $ which which /usr/local/bin/which $ type which which is a tracked alias for /bin/which 

På Debian, /bin/which är ett /bin/sh -skript. I mitt fall är sh dash men det är detsamma när det ”s bash.

En avstängd PATH är inte att inaktivera PATH sökning, men betyder att använda systemet ”s standard PATH som tyvärr på Debian är ingen överens om (dash och bash har /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin, zsh har /bin:/usr/bin:/usr/ucb:/usr/local/bin, ksh93 har /bin:/usr/bin, mksh har /usr/bin:/bin ($(getconf PATH)), execvp() (som i env) har :/bin:/usr/bin (ja, ser först i den aktuella katalogen!)) .

Därför får which fel ovan eftersom den använder dash ”s standard PATH vilket skiljer sig från ksh93 ”s

Det är ingen t bättre med GNU which som rapporterar:

which: no which in ((null)) 

(intressant, det finns verkligen en /usr/local/bin/which på mitt system som faktiskt är ett akanga skript som följde med akanga (ett rc skalderivat där standard PATH är /usr/ucb:/usr/bin:/bin:.))

bash, vilket system som helst:

Den som Chris hänvisar till i sitt svar :

$ 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 

Även efter att ha ringt hash manuellt:

$ 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 är det fall där which och ibland type misslyckas:

$ 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, med några skal:

$ foo bash: ./b/foo: /: bad interpreter: Permission denied 

Med andra:

$ foo a/foo 

Varken which eller type kan veta i förväg att b/foo inte kan b exekveras. Vissa skal som bash, ksh eller yash, när du anropar foo försöker verkligen köra b/foo och rapportera ett fel, medan andra (som zsh, ash, csh, Bourne, tcsh) körs a/foo vid misslyckandet av execve() systemanrop b/foo.

Kommentarer

  • mksh använder faktiskt något annat för standard $PATH: först används operativsystemets sammanställningstidskonstant _PATH_DEFPATH (oftast på BSD: erna), sedan används confstr(_CS_PATH, …) (POSIX), och om båda inte finns eller misslyckas används /bin:/usr/bin:/sbin:/usr/sbin.
  • I ditt första exempel, även om ls är en funktion som den används i g ls från PATH. Och which är bra att berätta vilken som används /usr/bin/ls eller /usr/local/bin/ls. Jag ser inte ’ ” Varför inte använda vilken ” ….
  • @rudimeier, Att which ls ger mig /bin/ls oavsett om ls -funktionsanrop /bin/ls eller /opt/gnu/bin/ls eller dir eller ingenting alls. IOW, which (det som implementeringar, IMMV) ger något irrelevant
  • @St é phaneChazelas. Nej nej nej. Jag vet redan att min ls är en funktion. Jag vet att min ls -funktion ringer till ls från PATH. Nu säger which var filen är. Du ser bara ett enstaka fall: ” Vad skulle mitt skal göra med det här kommandot. ” För detta användningsfall which är fel, rätt.Men det finns andra användningsfall där (GNU) which är exakt rätt sak.
  • @rudimeter beror på which implementering. Vissa kommer att säga att ’ är ett alias (om du har konfigurerat ett alias eller om det finns ett ~/.cshrc i ditt hem som har ett sådant alias), vissa ger dig en väg men fel under vissa förhållanden. sh -c 'command -v ls', men inte perfekt är fortfarande mer sannolikt att ge dig rätt svar på det annorlunda kravet (och är också standard).

Svar

En sak som (från mitt snabba skum) verkar som att Stephane inte nämnde är att which har ingen aning om ditt skal ”hash-bord”. Detta har den effekten att det kan returnera ett resultat som inte är representativt för vad som faktiskt körs, vilket gör det ineffektivt i felsökning.

Svar

Jag kryper vanligtvis när den här frågan rekommenderas till intet ont anande användare eftersom grundlös bashing på which inte är användbar för någon.

Om which fungerar bra och ger rätt svar på någon uppgift, efter Unix-moto: gör en sak, gör det bra , varför ska which vara förbjuden?

Frågan ska då vara vilken som fungerar bra och gör ett specifikt jobb bra?

För en, det externa verktyget vid / bin / vilket i Debian är ett skalskript vars mål bara är att lista körbara filer med det angivna namnet på sökvägen. Jag tror att which gör sitt avsedda mål korrekt. Det laddar inga alias, inga funktioner, ingenting från skalet, bara listar de första (eller alla) körbarheterna med det angivna namnet på PATH. som betyder för att ha hittat en fil med samma namn som angiven är något som användaren bör räkna ut av henne (honom) själv.

Ja, andra which implementeringar kan (och vanligtvis gör) ha särskilda problem.

Svar

Vi hör ofta det som bör undvikas. Varför? Vad ska vi använda istället?

Det har jag aldrig hört. Ge specifika exempel. Jag skulle oroa mig för din linuxdistribution och installerade mjukvarupaket, eftersom det är där which kommer ifrån!

SLES 11,4 x86-64

i tcsh-version 6.18.01:

> which which which: shell built-in command. 

i bash-version 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 är en del av util-linux ett standardpaket distribuerat av Linux Kernel Organization för användning som en del av Linux-operativsystemet. Det ger också dessa andra filer

/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 

min util-linux är version 2.19. Utgivningsanmärkningar kan enkelt hittas tillbaka till v2.13 daterad (28-aug-2007). Inte säker på vad poängen eller målet med detta var, det besvarades verkligen inte den långa saken som röstades upp 331 gånger.

Kommentarer

  • Lägg märke till hur frågan nämns inte vad Unix det hänvisar till. Linux är bara ett av några få.
  • Som din which -v visar, är ’ GNU som (den extravaganta en nämns i det andra svaret och är inte på något sätt specifik för Linux), inte util-linux som AFAIK aldrig inkluderade ett which -verktyg. util-linux 2.19 är från 2011, GNU som 2.19 är från 2008.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *