Hvorfor ikke bruke “ hvilken ”? Hva skal jeg bruke da?

Når du ser etter stien til en kjørbar eller sjekker hva som ville skje hvis du skriver inn et kommandonavn i et Unix-skall, er det en mengde forskjellige verktøy ( which, type, command, whence, where, whereis, whatis, hash osv.).

Vi hører ofte at which bør unngås. Hvorfor? Hva skal vi bruke i stedet?

Kommentarer

  • Jeg tror de fleste argumentene mot å bruke which antar en interaktiv skallkontekst. Dette spørsmålet er merket / bærbar. Så jeg tolker spørsmålet i denne sammenhengen som » hva du skal bruke i stedet for which for å finne den første kjørbare filen av et gitt navn i $PATH «. De fleste svar og grunner mot which tar for seg aliaser, innebygde funksjoner, som i de fleste bærbare skallskripter fra den virkelige verden bare er av akademisk interesse. Lokalt definerte aliaser er ikke ‘ t arvet når du kjører et skallskript (med mindre du kilder det med .).
  • @MattBianco, ja, csh (og which er fremdeles et csh -skript på de fleste kommersielle Unices) leser ~/.cshrc når de ikke er interaktive. At ‘ er grunnen til at du ‘ merker at csh-skript vanligvis starter med #! /bin/csh -f. which ikke fordi det har som mål å gi deg aliasene, fordi det ‘ er ment som et verktøy for (interaktive) brukere av csh. POSIX-skjellbrukere har command -v.
  • @rudimeier, så vil svaret være alltid med mindre skallet ditt er (t)csh (eller du har ikke ‘ t sinn hvis det ikke ‘ ikke gir deg riktig resultat), bruk type eller command -v i stedet . Se svarene for hvorfor .
  • @rudimeier, (stat $(which ls) er feil av flere grunner (mangler --, manglende anførselstegn), ikke bare bruken av which). Du ‘ bruker stat -- "$(command -v ls)". Det antar at ls faktisk er en kommando som finnes på filsystemet (ikke innebygd i skallet, eller aliasfunksjon). which kan gi deg feil bane (ikke banen som skallet ditt ville utført hvis du skrev inn ls) eller gi deg et alias som definert i konfigurasjonen av noen andre skjell …
  • @rudimeier, igjen, det er en rekke forhold der mange which implementeringer ikke vil gi deg engang ls som vil bli funnet ved et oppslag av $PATH (uansett hva ls kan påberope seg i skallet ditt). sh -c 'command -v ls', eller zsh -c 'rpm -q --whatprovides =ls' er mer sannsynlig å gi deg riktig svar. Poenget her er at which er en ødelagt arv fra csh.

Svar

Her er alt du aldri trodde du aldri ville ønske å vite om det:

Sammendrag

For å få stinavn på en kjørbar i et Bourne-lignende skallskript (det er noen advarsler; se nedenfor):

ls=$(command -v ls) 

For å finne ut om en gitt kommando eksisterer:

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

Ved ledeteksten fra et interaktivt Bourne-lignende skall:

type ls 

The which -kommandoen er en ødelagt arv fra C-Shell og er bedre å være alene i Bourne-lignende skjell.

Bruk tilfeller

Der «et skille mellom å lete etter den informasjonen som en del av et skript eller interaktivt på shell-ledeteksten.

Ved shell-ledeteksten er den typiske brukssaken: denne kommandoen oppfører seg merkelig, bruker jeg riktig? Hva skjedde akkurat da jeg skrev mycmd? Kan jeg se nærmere på hva det er?

I så fall vil du vite hva skallet ditt gjør når du påkaller kommandoen uten å påkalle kommandoen.

I skallskript har det en tendens til å være ganske annerledes. I et skallskript er det ingen grunn til at du vil vite hvor eller hva en kommando er hvis alt du vil gjøre er å kjøre den. Generelt er det du vil vite banen til den kjørbare filen, slik at du kan få mer informasjon ut av den (som banen til en annen fil i forhold til den, eller lese informasjon fra innholdet i den kjørbare filen på den banen).

Interaktivt vil du kanskje vite om alle my-cmd kommandoene som er tilgjengelige på systemet, i skript, sjelden det.

De fleste tilgjengelige verktøy (som ofte er tilfelle) er designet for å brukes interaktivt.

Historie

Litt historie først.

De tidlige Unix-skjellene til slutten av 70-tallet hadde ingen funksjoner eller aliaser. Bare den tradisjonelle oppslaget av kjørbare filer i $PATH. csh introduserte aliaser rundt 1978 (skjønt csh ble først utgitt i 2BSD, i mai 1979), og også behandlingen av en .cshrc for brukere å tilpasse skallet (hvert skall, som csh , leser .cshrc selv når det ikke er interaktivt som i skript).

Mens Bourne-skallet først ble utgitt i Unix V7 tidligere i 1979, ble funksjonsstøtte bare lagt til mye senere (1984 i SVR2), og uansett, den hadde aldri noen rc -fil (.profile er å konfigurere miljøet ditt, ikke skallet per se ).

csh ble mye mer populært enn Bourne-skallet som (selv om det hadde en forferdelig dårligere syntaks enn Bourne shell) tilføyde det mange mer praktiske og fine funksjoner for interaktiv bruk.

I 3BSD (1980), en which csh-skript ble lagt til for csh -brukerne for å hjelpe til med å identifisere en kjørbar fil, og det er et knapt annet skript du kan finne som which på mange kommersielle enheter i dag (som Solaris, HP / UX, AIX eller Tru64).

Dette skriptet leser brukeren» s ~/.cshrc (som alle csh -skript gjør med mindre de er påkalt med csh -f), og slår opp de angitte kommandonavnene i listen over aliaser og i $path (matrisen som csh opprettholder basert på $PATH).

Her går du: which kom først for det mest populære skallet på den tiden (og csh var fortsatt populært til midten av 90-tallet), som er den viktigste grunnen til at den ble dokumentert i bøker og fortsatt er mye brukt.

Merk at selv for en csh -bruker, at which csh-skript gir deg ikke nødvendigvis riktig informasjon. Det får aliasene definert i ~/.cshrc, ikke de du kanskje har definert senere ved ledeteksten, eller for eksempel ved source ved en annen csh fil, og (selv om det ikke ville være en god ide), kan PATH omdefineres i ~/.cshrc.

Å kjøre den which -kommandoen fra et Bourne-skall vil fremdeles slå opp aliaser som er definert i ~/.cshrc Hvis du ikke har en fordi du ikke bruker csh, vil det likevel sannsynligvis gi deg riktig svar.

En lignende funksjonalitet ble ikke lagt til Bourne-skallet fram til 1984 i SVR2 med type innebygd kommando. Det faktum at den er innebygd (i motsetning til et eksternt skript) betyr at den kan gi deg riktig informasjon (til en viss grad) ettersom den har tilgang til det indre av skallet.

Den opprinnelige type -kommandoen led av et lignende problem som which -skriptet ved at den ikke returnerte en feilutgangsstatus hvis kommandoen ble ikke funnet. Også for kjørbare filer, i motsetning til which, sendes den ut som ls is /bin/ls i stedet for bare /bin/ls som gjorde det mindre enkelt å bruke i skript.

Unix versjon 8″ s (ikke utgitt i naturen) Bourne-skall hadde det «s type innebygd omdøpt til whatis. Og Plan9 (den en gang etterfølgeren til Unix) skallet rc (og dens derivater som akanga og es) har også whatis.

Korn-skallet (et delsett av hvilket POSIX sh definisjon er basert på), utviklet på midten av 80-tallet, men ikke allment tilgjengelig før 1988, la til mange av csh -funksjonene ( linjeditor, aliaser …) på toppen av Bourne-skallet. Den la til sin egen whence innebygd (i tillegg til type) som tok flere alternativer (-v for å gi type -lignende verbose utdata, og -p bare for å se etter kjørbare filer (ikke aliaser / funksjoner …)) .

Tilfeldig med uroen med hensyn til copyright-spørsmålene mellom AT & T og Berkeley, noen få gratis programvare skallimplementeringer kom ut på slutten av 80-tallet tidlig på 90-tallet.Alt Almquist-skallet (ash, som erstatning for Bourne-skallet i BSD-er), implementeringen av det offentlige området av ksh (pdksh), bash (sponset av FSF), zsh kom ut mellom 1989 og 1991.

Ash, men ment å være en erstatning for Bourne-skallet, hadde ikke en type innebygd til mye senere (i NetBSD 1.3 og FreeBSD 2.3 ), selv om den hadde hash -v. OSF / 1 /bin/sh hadde en type innebygd som alltid returnerte 0 opp til OSF / 1 v3.x. bash la ikke til en whence men la til en -p alternativ til type for å skrive ut banen (type -p vil være som whence -p) og -a for å rapportere alle samsvarende kommandoer. tcsh laget which innebygd og la til en where kommando som fungerte som bash» s type -a. zsh har dem alle.

fish shell (2005) har en type -kommando implementert som en funksjon.

which csh-skript ble i mellomtiden fjernet fra NetBSD (ettersom det var innebygd i tcsh og ikke brukte mye andre skjell), og funksjonaliteten ble lagt til whereis (når påkalt som which, whereis oppfører seg som which bortsett fra at den bare ser opp kjørbare filer i iv id = «00879d7bee» I OpenBSD og FreeBSD ble which også endret til en skrevet i C som bare ser på kommandoer i $PATH .

Implementeringer

Det er dusinvis av implementeringer av en which co mmog på forskjellige enheter med forskjellig syntaks og oppførsel.

På Linux (ved siden av de innebygde i tcsh og zsh ) finner vi flere implementeringer. På nyere Debian-systemer er det for eksempel et enkelt POSIX-skallskript som ser etter kommandoer i $PATH.

busybox har også en which kommando.

Det finnes en GNU which som sannsynligvis er den mest ekstravagante. Den prøver å utvide hva which csh-skriptet gjorde til andre skjell: du kan fortelle det hva aliasene og funksjonene dine er, slik at det kan gi du får et bedre svar (og jeg tror noen Linux-distribusjoner setter noen globale aliaser rundt det for bash for å gjøre det).

zsh har et par operatorer for å utvide til banen til kjørbare filer: operatøren = filnavnutvidelse og :c historieutvidelsesmodifikator (her brukt på parameterutvidelse ):

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

zsh, i -modul lager også kommandoen hash-tabellen som commands assosierende matrise:

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

whatis verktøyet (bortsett fra det i Unix V8 Bourne-skallet eller Plan 9 rc / es) er egentlig ikke relatert, da det bare er til dokumentasjon (griper whatis-databasen, det vil si mansidesynopsis «).

whereis var også lagt til 3BSD samtidig som which selv om det ble skrevet i C, ikke csh og brukes til å slå opp samtidig, den kjørbare, man-siden og kilden, men ikke basert på gjeldende miljø. Så igjen, det svarer på et annet behov.

Nå, på standardfronten, spesifiserer POSIX command -v og -V kommandoer (som tidligere var valgfrie fram til POSIX.2008). UNIX angir kommandoen type (ikke noe alternativ). At «s all (where, which, whence ikke er spesifisert i noen standard) .

Opp til en eller annen versjon var type og command -v valgfrie i Linux Standard Base-spesifikasjonen som forklarer hvorfor for eksempel noen gamle versjoner av posh (skjønt basert på pdksh som hadde begge deler) hadde ikke begge. command -v ble også lagt til i noen Bourne-skallimplementeringer (som på Solaris).

Status i dag

Status i dag er at type og command -v er allestedsnærværende de Bourne-lignende skjellene (skjønt, som bemerket av @jarno, legg merke til advarselen / feilen i bash når du ikke er i POSIX-modus eller noen etterkommere av Almquist-skallet nedenfor i kommentarer). tcsh er det eneste skallet der du vil bruke which (da det ikke er type der og which er innebygd).

I andre skjell enn tcsh og zsh, which kan fortelle deg banen til den gitte kjørbare filen så lenge det ikke er noe alias eller funksjon med det samme navnet i noen av våre ~/.cshrc, ~/.bashrc eller en hvilken som helst shell-oppstartsfil, og du definerer ikke $PATH i ~/.cshrc. Hvis du har et alias eller en funksjon som er definert for det, kan det fortelle deg om det eller ikke fortelle deg det.

Hvis du vil vite omtrent alle kommandoene med et gitt navn, det er ingenting bærbart. Du ville bruke where i tcsh eller zsh, type -a i bash eller zsh, whence -a i ksh93 og i andre skjell , kan du bruke type i kombinasjon med which -a som kan fungere.

Anbefalinger

Få stienavnet til en kjørbar

Nå, for å få stinavnet til en kjørbar i et skript, er det noen advarsler:

ls=$(command -v ls) 

ville være den vanlige måten å gjøre det på.

Det er imidlertid noen få problemer:

  • Det er ikke mulig å vite banen til den kjørbare filen uten å utføre den. type, which, command -v … alle bruker heuristikk for å finne veien De går gjennom $PATH -komponentene og finner den første ikke-katalogfilen du har tillatelse til. Imidlertid depe når det gjelder å utføre kommandoen, vil mange av dem (Bourne, AT & T ksh, zsh, ash …) bare henrette dem i rekkefølgen av $PATH til execve systemanropet returnerer ikke med en feil. For eksempel hvis $PATH inneholder /foo:/bar og du vil utføre ls, vil de først prøve å utføre /foo/ls eller hvis det mislykkes /bar/ls. Nå kan utførelsen av /foo/ls mislykkes fordi du ikke har kjøringstillatelse, men også av mange andre grunner, som om den ikke er en gyldig kjørbar. command -v ls ville rapportere /foo/ls hvis du har kjøringstillatelse for /foo/ls, men kjører ls kan faktisk kjøre /bar/ls hvis /foo/ls ikke er en gyldig kjørbar.
  • hvis foo er en innebygd funksjon eller alias, returnerer command -v foo foo. Med noen skall som ash, pdksh eller zsh, kan det også returnere foo hvis $PATH inkluderer den tomme strengen og det er en kjørbar foo -fil i gjeldende katalog. Det er noen omstendigheter der du må ta hensyn til det. Husk for eksempel at listen over innebygde programmer varierer med skallimplementeringen (for eksempel er mount noen ganger innebygd for opptattboks sh), og for eksempel bash kan få funksjoner fra miljøet.
  • hvis $PATH inneholder relative banekomponenter (vanligvis . eller den tomme strengen som begge refererer til den nåværende katalogen, men kan være hva som helst), avhengig av skallet, command -v cmd sender kanskje ikke ut en absolutt bane. Så banen du får på det tidspunktet du kjører vil ikke lenger være gyldig etter at du cd et annet sted.
  • Anekdotisk: med ksh93-skallet, hvis /opt/ast/bin (selv om den nøyaktige banen kan variere på forskjellige systemer tror jeg) er i deg $PATH, ksh93 vil gjøre tilgjengelig noen ekstra innebygde (chmod, cmp, cat …), men command -v chmod vil returnere /opt/ast/bin/chmod selv om den banen ikke eksisterer.

Bestemme om en kommando eksisterer

For å finne ut om en gitt kommando eksisterer standard, kan du gjøre:

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

Hvor man kanskje vil bruke which

(t)csh

I csh og tcsh har du ikke mye valg. I tcsh, at «s fine as which er innebygd. I csh vil det være systemet which -kommandoen, som kanskje ikke gjør det du vil i noen få tilfeller.

Finn kommandoer bare i noen skall

Et tilfelle der det kan være fornuftig å bruke which er hvis du vil vite banen til en kommando, ignorerer potensialet skallinnbygginger eller funksjoner i bash, csh (ikke tcsh), dash, eller Bourne skallskripter, det vil si skjell som ikke har whence -p (som ksh eller zsh), command -ev (som yash ), whatis -p (rc, akanga) eller en innebygd which (som tcsh eller zsh) på systemer der which tilgjengelig og er ikke csh script.

Hvis disse vilkårene er oppfylt, vil:

echo=$(which echo) 

gi deg veien til den første echo i $PATH (unntatt i hjørnetilfeller), uansett om echo tilfeldigvis også er et skall innebygd / alias / funksjon eller ikke.

I andre skjell foretrekker du:

  • 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` (pass på stier med mellomrom)
  • fisk : set echo (type -fp echo)

Merk at hvis alt du vil gjøre er kjør at echo -kommandoen, du trenger ikke å få veien, du kan bare gjøre:

env echo this is not echoed by the builtin echo 

For eksempel, med tcsh, for å forhindre at den innebygde which blir brukt:

set Echo = "`env which echo`" 

Når du trenger en ekstern kommando

Et annet tilfelle der du kanskje vil bruke which er når du faktisk trenger en ekstern kommando. POSIX krever at alle shell-innebygde (som command) også er tilgjengelige som eksterne kommandoer, men dessverre er det ikke tilfelle for command på mange systemer. For eksempel er det sjelden å finne en command -kommando på Linux-baserte operativsystemer, mens de fleste av dem har en which kommando (skjønt forskjellige med forskjellige alternativer og oppførsel).

Tilfeller der du kanskje vil ha en ekstern kommando, vil være hvor du vil utføre en kommando uten å påkalle et POSIX-skall.

system("some command line"), popen() … funksjoner til C eller forskjellige språk påkaller et skall for å analysere den kommandolinjen, så system("command -v my-cmd") fungerer i dem. Et unntak fra det ville være perl som optimaliserer skallet hvis det ikke ser noe skallets spesialtegn (annet enn mellomrom). Det gjelder også backtick-operatøren:

$ 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 

Tillegget av det :; over tvinger perl til å påkalle et skall der. Ved å bruke which, trenger du ikke å bruke det trikset.

Kommentarer

  • @Joe, which er et csh -skript på mange kommersielle enheter. Årsaken er historisk, at ‘ hvorfor jeg ga historien, så folk forstår hvor den kom fra, hvorfor folk ble vant til å bruke den og hvorfor det faktisk var ‘ er ingen grunn til at du skal bruke den. Og ja, noen bruker (t) csh. Ikke alle bruker Linux ennå
  • Etter å ha lest dette innlegget, har jeg funnet mye sammenheng for svaret, men ikke selve svaret.Hvor i dette innlegget står det faktisk hvorfor ikke å bruke which, i motsetning til ting du kanskje prøver å bruke which å gjøre, historien til which, implementeringer av which, andre kommandoer for å gjøre relaterte oppgaver, eller grunner til å faktisk bruke which? Hvorfor er de andre kommandoene bedre ? Hva gjør de annerledes enn which? Hvordan unngår de fallgruvene? Dette svaret bruker faktisk flere ord på problemene med alternativene enn problemene med which.
  • command er beskrevet av POSIX.
  • @St é phaneChazelas Hvis jeg oppretter en ny fil med touch /usr/bin/mytestfile og deretter kjører command -v mytestfile, det vil gi banen (mens which mytestfile ikke gjør det.)
  • @jarno, å ja, du ‘ har rett. bash vil slå seg til ro i en ikke-kjørbar fil hvis den ‘ ikke finner en kjørbar fil, så den ‘ s » OK » (skjønt i praksis vil man heller command -v / type returnerer en feil) som at ‘ er kommandoen den vil prøve å utføre når du kjører mytestfile, men dash oppførsel er ujevn, som om det ‘ er en ikke-kjørbar cmd foran en kjørbar, returnerer command -v den ikke-kjørbare mens utføring cmd ville kjøre den kjørbare (feil en er også hash). FreeBSD sh (også basert på ash) har samme feil. zsh, yash, ksh, mksh, bash som sh er OK.

Svar

Årsakene til at man kan ikke ønsker å bruke which har allerede blitt forklart, men her er noen eksempler på noen få systemer der which faktisk mislykkes.

På Bourne-lignende skjell sammenligner vi utdata fra which med utdata fra type (type som et skall innebygd, er det ment å være grunn sannheten, da det er skallet som forteller oss hvordan det vil påkalle en kommando).

Mange tilfeller er hjørne saker, men husk at which / type ofte brukes i hjørnesaker (for å finne svaret til en uventet oppførsel som: hvorfor i all verden oppfører den kommandoen seg slik, hvilken ringer jeg til? ).

De fleste systemer, de fleste Bourne-lignende skjell: funksjoner

Det mest åpenbare tilfellet er for funksjoner:

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

Årsaken er at which bare rapporterer om kjørbare filer, og noen ganger om aliaser (men ikke alltid de fra ditt skallet), ikke funksjoner.

GNU som man-siden har et ødelagt (da de glemte å sitere $@) eksempel på hvordan man kan bruke den til å rapportere funksjoner også, men akkurat som for aliaser, fordi den ikke implementerer en skallsyntaks-parser, blir den lett lurt:

$ 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 fleste systemer, de fleste Bourne-lignende skjell: innebygd

Et annet åpenbart tilfelle er innebygde eller nøkkelord, da which å være en ekstern kommando, har ingen måte å vite hvilke innebygde skallet ditt har (og noen skall som zsh, bash eller ksh kan laste innebygde dynamisk):

$ 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 

(som ikke gjelder zsh der which er innebygd)

Solaris 10, AIX 7.1, HP / UX 11i, Tru64 5. 1 og mange andre:

$ 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 er fordi de fleste kommersielle enheter, which (som i den opprinnelige implementeringen på 3BSD) er et csh skript som leser ~/.cshrc. Aliasene den vil rapportere er de som er definert der uavhengig av aliasene du har definert for øyeblikket, og uansett skallet du faktisk bruker.

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- og AIX-versjonene har løst problemet ved å lagre $path før du leser ~/.cshrc og gjenopprette den før du ser på kommandoen (e))

$ 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 

(selvfølgelig, hvis du er et csh -skript, kan du ikke forvente at det fungerer med argumenter som inneholder mellomrom …)

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 er det et alias-definert systemomfattende som omgir GNU which -kommandoen.

Den falske utgangen er fordi which leser utdataene fra bash «s alias men vet ikke hvordan man skal analysere det riktig og bruker heuristikk (ett alias per linje, ser etter den første funnet kommandoen etter en |, ;, & …)

Det verste på CentOS er at zsh har en helt fin which innebygd kommando, men CentOS klarte å bryte den ved å erstatte den med et ikke-fungerende alias til GNU which.

Debian 7.0, ksh93:

(men gjelder de fleste systemer med mange skall)

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

På Debian, /bin/which er et /bin/sh skript. I mitt tilfelle er sh dash men det er det samme når det «s bash.

En usett PATH er ikke til å deaktivere PATH oppslag, men betyr å bruke systemet «s standard PATH som dessverre på Debian ikke er enige om (dash og 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 gjeldende katalog!)) .

Det er grunnen til at which gjør det galt ovenfor, siden det bruker dash «s standard PATH som er forskjellig fra ksh93 «s

Det er nei t bedre med GNU which som rapporterer:

which: no which in ((null)) 

(interessant, det er faktisk en /usr/local/bin/which på systemet mitt som faktisk er et akanga skript som fulgte med akanga (et rc skallderivat der standard PATH er /usr/ucb:/usr/bin:/bin:.))

bash, hvilket som helst system:

Den Chris refererer til 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 

Også etter å ha ringt hash manuelt:

$ 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) 

Nå er et tilfelle der which og noen ganger type mislykkes:

$ 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 

Nå, med noen skall:

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

Med andre:

$ foo a/foo 

Verken which eller type kan vite på forhånd at b/foo ikke kan b e henrettet. Noen skall som bash, ksh eller yash, når du påkaller foo vil faktisk prøve å kjøre b/foo og rapportere en feil, mens andre (som zsh, ash, csh, Bourne, tcsh) vil kjøre a/foo ved feil på execve() systemanrop b/foo.

Kommentarer

  • mksh bruker faktisk noe annet for standard $PATH: først , blir operativsystemets kompileringstidskonstant _PATH_DEFPATH brukt (oftest på BSD-er), så brukes confstr(_CS_PATH, …) (POSIX), og hvis begge ikke eksisterer eller mislykkes, brukes /bin:/usr/bin:/sbin:/usr/sbin.
  • I ditt første eksempel, selv om ls er en funksjon den brukes i g ls fra PATH. Og which er greit å fortelle deg hvilken som brukes /usr/bin/ls eller /usr/local/bin/ls. Jeg ser ikke ‘ t » Hvorfor ikke bruke hvilken » ….
  • @rudimeier, At which ls vil gi meg /bin/ls uansett om ls funksjonskaller /bin/ls eller /opt/gnu/bin/ls eller dir eller ingenting. IOW, which (det som implementeringer, IMMV) gir noe irrelevant
  • @St é phaneChazelas. Nei nei nei. Jeg vet allerede at ls er en funksjon. Jeg vet at ls -funksjonen min ringer til ls fra PATH. Nå forteller which hvor filen er. Du ser bare ett engangsbruk: » Hva ville skjellet mitt gjort med denne kommandoen. » For dette brukstilfellet which er feil, riktig.Men det er andre brukstilfeller der (GNU) which er akkurat det rette.
  • @rudimeter, avhenger av which implementering. Noen vil fortelle deg at ‘ er et alias (hvis du har konfigurert et alias, eller hvis det er et ~/.cshrc i hjemmet ditt som har et slikt alias), noen vil gi deg en vei, men den gale under noen forhold. sh -c 'command -v ls', men ikke perfekt er fremdeles mer sannsynlig å gi deg riktig svar på det forskjellige kravet (og er også standard).

Svar

En ting som (fra mitt raske skum) ser ut til at Stephane ikke nevnte, er at which har ingen anelse om skallets sti-hash-bord. Dette har den effekten at det kan gi et resultat som ikke er representativt for det som faktisk kjøres, noe som gjør det ineffektivt i feilsøking.

Svar

Jeg kryper vanligvis når dette spørsmålet anbefales til intetanende brukere fordi grunnløs bashing på which ikke er nyttig for noen.

Hvis which fungerer bra og gir det riktige svaret på noen oppgaver ved å følge Unix-motoen: gjør en ting, gjør det bra , hvorfor skulle which være utestengt?

Spørsmålet bør da være, hvem som fungerer bra og gjør en bestemt jobb bra?

For det ene, det eksterne verktøyet på / bin / som i Debian er et skallskript som bare har som mål å føre kjørbare filer med gitt navn på banen. Jeg tror at which gjør sitt tiltenkte mål riktig. Den laster ingen aliaser, ingen funksjoner, ingenting fra skallet, bare viser de første (eller alle) kjørbarhetene med gitt navn på PATH. som betyr for å ha funnet en fil med samme navn som gitt, er noe som brukeren skal finne ut av henne (ham) selv.

Ja, andre which implementeringer kan (og vanligvis ha) spesielle problemer.

Svar

Vi hører ofte det som bør unngås. Hvorfor? Hva skal vi bruke i stedet?

Det har jeg aldri hørt. Gi spesifikke eksempler. Jeg vil bekymre deg for linux-distribusjonen og installerte programvarepakker, for så vidt kommer fra which!

SLES 11.4 x86-64

i tcsh versjon 6.18.01:

> which which which: shell built-in command. 

i bash-versjon 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 er en del av util-linux en standardpakke distribuert av Linux Kernel Organization for bruk som en del av Linux-operativsystemet. Det gir også disse andre filene

/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 er versjon 2.19. Versjonsmerknader kan lett bli funnet tilbake til v2.13 datert (28. august 2007). Ikke sikker på hva poenget eller målet med dette var, det ble absolutt ikke besvart den lange saken som ble oppstemt 331 ganger.

Kommentarer

  • Legg merke til hvordan spørsmålet nevner ikke hva Unix det refererer til. Linux er bare en av få.
  • Som din which -v viser, er ‘ GNU som (det ekstravagante en nevnt i det andre svaret og er på ingen måte spesifikk for Linux), ikke util-linux som AFAIK aldri inkluderte et which verktøy. util-linux 2.19 er fra 2011, GNU som 2.19 er fra 2008.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *