Perché non usare "quale"? Cosa usare allora?


328

Quando si cerca il percorso di un file eseguibile o il controllo che cosa accadrebbe se si immette un nome di comando in una shell Unix, c'è una pletora di diverse utenze ( which, type, command, whence, where, whereis, whatis, hash, ecc).

Sentiamo spesso che whichdovrebbe essere evitato. Perché? Cosa dovremmo usare invece?


3
Penso che la maggior parte degli argomenti contro l'uso whichstia assumendo un contesto di shell interattivo. Questa domanda è taggata / portabilità. Quindi interpreto la domanda in questo contesto come "cosa usare invece di whichtrovare il primo eseguibile di un determinato nome nel $PATH". La maggior parte delle risposte e delle ragioni contro l' whichaffare con alias, builtin e funzioni, che nella maggior parte degli script di shell portatili del mondo reale sono solo di interesse accademico. Gli alias definiti localmente non vengono ereditati quando si esegue uno script di shell (a meno che non venga sorgente con .).
MattBianco,

5
@MattBianco, sì, csh(ed whichè ancora uno cshscript sulla maggior parte degli Unici commerciali) legge ~/.cshrcquando non interattivo. Ecco perché noterai che gli script csh di solito iniziano con #! /bin/csh -f. whichnon perché mira a darti gli alias, perché è inteso come uno strumento per utenti (interattivi) di csh. Gli utenti di shell POSIX hanno command -v.
Stéphane Chazelas,

@rudimeier, la risposta sarebbe sempre a meno che la tua shell non sia (t)csh(o non ti dispiaccia se non ti dà il risultato corretto), usa typeo command -vinvece . Vedi le risposte per il perché .
Stéphane Chazelas,

1
@rudimeier, ( stat $(which ls)è errato per diversi motivi (mancante --, virgolette mancanti), non solo l'uso di which). Useresti stat -- "$(command -v ls)". Ciò presuppone che lssia effettivamente un comando trovato nel file system (non un built-in della shell o una funzione di alias). whichpotrebbe darti il ​​percorso sbagliato (non il percorso che la tua shell eseguirà se tu entrassi ls) o darti un alias come definito nella configurazione di alcune altre shell ...
Stéphane Chazelas,

1
@rudimeier, ancora una volta, ci sono una serie di condizioni in cui molte whichimplementazioni non ti darebbero nemmeno quello lsche verrebbe trovato da una ricerca di $PATH(indipendentemente da ciò che lspuò invocare nella tua shell). sh -c 'command -v ls'o zsh -c 'rpm -q --whatprovides =ls'hanno maggiori probabilità di darti la risposta corretta. Il punto qui è che whichè un'eredità rotta da csh.
Stéphane Chazelas,

Risposte:


366

Ecco tutto ciò che non avresti mai pensato di non voler sapere al riguardo:

Sommario

Per ottenere il percorso di un eseguibile in uno script di shell tipo Bourne (ci sono alcuni avvertimenti; vedi sotto):

ls=$(command -v ls)

Per scoprire se esiste un determinato comando:

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

Al prompt di una shell interattiva simile a Bourne:

type ls

Il whichcomando è un'eredità spezzata dalla C-Shell ed è meglio lasciarlo solo nelle shell tipo Bourne.

Casi d'uso

Esiste una distinzione tra la ricerca di tali informazioni come parte di uno script o in modo interattivo al prompt della shell.

Al prompt della shell, il tipico caso d'uso è: questo comando si comporta in modo strano, sto usando quello giusto? Cosa è successo esattamente quando ho scritto mycmd? Posso guardare oltre di cosa si tratta?

In quel caso, vuoi sapere cosa fa la tua shell quando invochi il comando senza effettivamente invocare il comando.

Negli script di shell, tende ad essere abbastanza diverso. In uno script di shell non c'è motivo per cui si vorrebbe sapere dove o cosa sia un comando se tutto ciò che si desidera fare è eseguirlo. Generalmente, quello che vuoi sapere è il percorso dell'eseguibile, in modo da poter ottenere maggiori informazioni da esso (come il percorso di un altro file relativo a quello, o leggere informazioni dal contenuto del file eseguibile in quel percorso).

In modo interattivo, potresti voler conoscere tutti i my-cmdcomandi disponibili sul sistema, negli script, raramente.

La maggior parte degli strumenti disponibili (come spesso accade) sono stati progettati per essere utilizzati in modo interattivo.

Storia

Prima un po 'di storia.

I primi gusci Unix fino alla fine degli anni '70 non avevano funzioni o alias. Solo la tradizionale ricerca di eseguibili in $PATH. cshintrodusse alias intorno al 1978 (anche se cshfu pubblicato per la prima volta nel 2BSDmaggio 1979) e anche l'elaborazione di un .cshrcper gli utenti per personalizzare la shell (ogni shell, come si cshlegge .cshrcanche quando non interattiva come negli script).

mentre la shell Bourne è stata rilasciata per la prima volta in Unix V7 all'inizio del 1979, il supporto delle funzioni è stato aggiunto solo molto più tardi (1984 in SVR2), e comunque non ha mai avuto alcun rcfile ( .profileè quello di configurare l'ambiente, non la shell in ).

csh è diventato molto più popolare della shell Bourne in quanto (sebbene avesse una sintassi terribilmente peggiore della shell Bourne) stava aggiungendo molte più funzioni utili e piacevoli per l'uso interattivo.

Nel 3BSD(1980) fu aggiunto uno whichscript csh per gli cshutenti per aiutare a identificare un eseguibile, ed è uno script difficilmente diverso che puoi trovare oggi whichsu molti Unices commerciali (come Solaris, HP / UX, AIX o Tru64).

Tale script legge l'utente ~/.cshrc(come fanno tutti gli cshscript se non viene invocato con csh -f) e cerca i nomi dei comandi forniti nell'elenco degli alias e in $path(l'array che cshmantiene basato su $PATH).

Ecco qui, è whicharrivato il primo per la shell più popolare al momento (ed cshera ancora popolare fino alla metà degli anni '90), che è il motivo principale per cui è stato documentato nei libri ed è ancora ampiamente usato.

Nota che, anche per un cshutente, quello whichscript csh non ti fornisce necessariamente le informazioni giuste. Ottiene gli alias definiti in ~/.cshrc, non quelli che potresti aver definito in seguito al prompt o, ad esempio, sourceingingendo un altro cshfile e (anche se non sarebbe una buona idea), PATHpotrebbe essere ridefinito ~/.cshrc.

Eseguire quel whichcomando da una shell Bourne, cercherebbe comunque gli alias definiti nel tuo ~/.cshrc, ma se non ne hai uno perché non lo usi csh, probabilmente ti darebbe comunque la risposta giusta.

Una funzionalità simile non fu aggiunta alla shell Bourne fino al 1984 in SVR2 con il typecomando incorporato. Il fatto che sia incorporato (al contrario di uno script esterno) significa che può darti le giuste informazioni (in una certa misura) in quanto ha accesso agli interni della shell.

Il typecomando iniziale presentava un problema simile a quello dello whichscript in quanto non restituiva uno stato di uscita non riuscita se il comando non veniva trovato. Inoltre, per gli eseguibili, al contrario which, ha prodotto qualcosa di simile ls is /bin/lsinvece di semplicemente /bin/lsche lo rendeva meno facile da usare negli script.

La shell Bourne di Unix versione 8 (non rilasciata in natura) aveva il typenome incorporato whatis. E anche la shell Plan9 (l'ex successore di Unix) rc(e i suoi derivati ​​come akangae es) whatis.

La shell Korn (un sottoinsieme su cui si basa la definizione sh POSIX), sviluppata a metà degli anni '80 ma non ampiamente disponibile prima del 1988, ha aggiunto molte delle cshfunzionalità (editor di linee, alias ...) in cima alla shell Bourne . Ha aggiunto il proprio whencebuiltin (oltre a type) che ha preso diverse opzioni ( -vper fornire l' typeoutput dettagliato simile e -pper cercare solo gli eseguibili (non alias / funzioni ...)).

In coincidenza con le turbolenze per quanto riguarda i problemi di copyright tra AT&T e Berkeley, alla fine degli anni '80 e all'inizio degli anni '90 sono emerse alcune implementazioni della shell del software libero . Tutta la shell Almquist (ash, in sostituzione della shell Bourne nei BSD), l'implementazione di dominio pubblico di ksh (pdksh), bash(sponsorizzata dalla FSF), zshè stata lanciata tra il 1989 e il 1991.

Ash, sebbene intendesse essere un sostituto della shell Bourne, non aveva un typebuiltin molto tempo dopo (in NetBSD 1.3 e FreeBSD 2.3), sebbene lo fosse hash -v. OSF / 1 /bin/shaveva un typebuiltin che restituiva sempre 0 fino a OSF / 1 v3.x. bashnon aggiungere un whencema ha aggiunto -pun'opzione per typestampare il percorso ( type -psarebbe come whence -p) e -adi segnalare tutti i comandi corrispondenti. tcshreso whichincorporato e aggiunto un wherecomando che agisce come quello bashdi type -a. zshli ha tutti.

La fishshell (2005) ha un typecomando implementato come funzione.

Lo whichscript csh nel frattempo è stato rimosso da NetBSD (poiché era incorporato in tcsh e di scarsa utilità in altre shell), e la funzionalità aggiunta a whereis(quando invocata come which, whereissi comporta come whichse non fosse che cerca solo gli eseguibili $PATH). In OpenBSD e FreeBSD, è whichstato anche cambiato in uno scritto in C che cerca solo i comandi $PATH.

implementazioni

Esistono dozzine di implementazioni di un whichcomando su vari Unices con sintassi e comportamento diversi.

Su Linux (oltre a quelli integrati in tcshe zsh) troviamo diverse implementazioni. Sui recenti sistemi Debian, ad esempio, è un semplice script shell POSIX che cerca i comandi $PATH.

busyboxha anche un whichcomando.

C'è uno GNU whichche è probabilmente il più stravagante. Cerca di estendere ciò che lo whichscript csh ha fatto ad altre shell: puoi dirgli quali sono i tuoi alias e funzioni in modo che possa darti una risposta migliore (e credo che alcune distribuzioni Linux impostino alcuni alias globali attorno a quello per bashfarlo) .

zshha un paio di operatori da espandere al percorso degli eseguibili: l' operatore di = espansione del nome file e il :cmodificatore di espansione della cronologia (qui applicato all'espansione dei parametri ):

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

zsh, nel zsh/parametersmodulo crea anche il comando hash table come commandsarray associativo:

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

L' whatisutilità (tranne quella nella shell Unix V8 Bourne o Plan 9 rc/ es) non è realmente correlata in quanto è solo per la documentazione (greps il database whatis, ovvero la sinossi della pagina man ').

whereisè stato anche aggiunto nello 3BSDstesso momento come whichse fosse stato scritto C, non cshed è usato per cercare allo stesso tempo, l'eseguibile, la pagina man e il sorgente ma non basati sull'ambiente attuale. Quindi di nuovo, questo risponde a un'esigenza diversa.

Ora, sul fronte standard, POSIX specifica i comandi command -ve -V(che erano opzionali fino a POSIX.2008). UNIX specifica il typecomando (nessuna opzione). Questo è tutto ( where, which, whencenon sono specificate in qualsiasi standard)

Fino ad alcune versioni, typeed command -verano opzionali nella specifica Base standard di Linux, il che spiega perché, ad esempio, alcune vecchie versioni di posh(sebbene basate su pdkshquale avevano entrambe) non avevano neanche. command -vè stato anche aggiunto ad alcune implementazioni della shell Bourne (come su Solaris).

Stato oggi

Al giorno d'oggi lo stato è quello typee command -vsono onnipresenti in tutte le shell tipo Bourne (sebbene, come notato da @jarno, notare l'avvertenza / bug in bashquando non in modalità POSIX o alcuni discendenti della shell Almquist in basso nei commenti). tcshè l'unica shell in cui vorresti usare which(dato che non typec'è e whichc'è incorporato).

Nei gusci diversi tcshe zsh, whichpuò dire il percorso di un determinato file eseguibile finché non c'è alcun alias o una funzione da quello stesso nome in una delle nostre ~/.cshrc, ~/.bashrco qualsiasi file di avvio della shell e non si definisce $PATHnel vostro ~/.cshrc. Se hai definito un alias o una funzione per esso, potrebbe parlarti o meno o dirti la cosa sbagliata.

Se vuoi sapere tutti i comandi con un determinato nome, non c'è nulla di portatile. Dovresti usare wherein tcsho zsh, type -ain basho zsh, whence -ain ksh93 e in altre shell, puoi usarlo typein combinazione con il which -aquale potrebbe funzionare.

raccomandazioni

Ottenere il nome del percorso in un eseguibile

Ora, per ottenere il percorso di un eseguibile in uno script, ci sono alcuni avvertimenti:

ls=$(command -v ls)

sarebbe il modo standard per farlo.

Ci sono alcuni problemi però:

  • Non è possibile conoscere il percorso dell'eseguibile senza eseguirlo. Tutti i type, which, command -v... tutti euristica usare per trovare il percorso. Eseguono il ciclo attraverso i $PATHcomponenti e trovano il primo file non di directory per il quale si dispone dell'autorizzazione di esecuzione. Tuttavia, a seconda della shell, quando si tratta di eseguire il comando, molti di essi (Bourne, AT&T ksh, zsh, ash ...) li eseguiranno solo nell'ordine $PATHfino a quando la execvechiamata di sistema non ritorna con un errore . Ad esempio, se $PATHcontiene /foo:/bare si desidera eseguire ls, tenteranno prima di eseguire /foo/lso se ciò fallisce /bar/ls. Ora esecuzione di/foo/lspotrebbe non riuscire perché non si dispone dell'autorizzazione di esecuzione, ma anche per molte altre ragioni, come se non fosse un eseguibile valido. command -v lssegnalerebbe /foo/lsse si dispone dell'autorizzazione di esecuzione per /foo/ls, ma l'esecuzione lspotrebbe effettivamente essere eseguita /bar/lsse /foo/lsnon è un eseguibile valido.
  • se fooè un builtin o una funzione o un alias, command -v foorestituisce foo. Con alcune shell come ash, pdksho zsh, può anche restituire foose $PATHinclude la stringa vuota e c'è un foofile eseguibile nella directory corrente. Ci sono alcune circostanze in cui potrebbe essere necessario tenerne conto. Tenere presente, ad esempio, che l'elenco dei builtin varia con l'implementazione della shell (ad esempio, a mountvolte è incorporato per busybox sh) e, ad esempio, bashpuò ottenere funzioni dall'ambiente.
  • se $PATHcontiene componenti di percorso relativi (in genere .o la stringa vuota che si riferiscono entrambi alla directory corrente ma potrebbero essere qualsiasi cosa), a seconda della shell, command -v cmdpotrebbe non generare un percorso assoluto. Quindi il percorso che ottieni nel momento in cui corri command -vnon sarà più valido dopo te cdda qualche altra parte.
  • Aneddotica: con il guscio ksh93, se /opt/ast/bin(anche se questo percorso esatto può variare su diversi sistemi credo) è in voi $PATH, ksh93 metterà a disposizione un paio di comandi incorporati in più ( chmod, cmp, cat...), ma command -v chmodrestituisce /opt/ast/bin/chmodanche se quel percorso doesn' non esiste.

Determinare se esiste un comando

Per scoprire se un determinato comando esiste in modo standard, puoi fare:

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

Dove uno potrebbe voler usare which

(t)csh

In cshe tcshnon hai molta scelta. In tcsh, va bene come whichè incorporato. In csh, quello sarà il whichcomando di sistema , che in alcuni casi potrebbe non fare quello che vuoi.

trova i comandi solo in alcune shell

Un caso in cui potrebbe avere senso usare whichè se si desidera conoscere il percorso di un comando, ignorando i potenziali incorporati o le funzioni della shell bash, csh(non tcsh) dash, o gli Bournescript di shell, ovvero le shell che non hanno whence -p(come ksho zsh) , command -ev(like yash), whatis -p( rc, akanga) o un builtin which(like tcsho zsh) su sistemi dove whichè disponibile e non è lo cshscript.

Se tali condizioni sono soddisfatte, allora:

echo=$(which echo)

ti darebbe il percorso del primo echoin $PATH(tranne nei casi d'angolo), indipendentemente dal fatto che echosi tratti o meno di una shell builtin / alias / function.

In altre shell, preferiresti:

  • zsh : echo==echooppure echo=$commands[echo]oppureecho=${${:-echo}:c}
  • ksh , zsh :echo=$(whence -p echo)
  • yash :echo=$(command -ev echo)
  • rc , akanga : echo=`whatis -p echo`(attenzione ai percorsi con spazi)
  • pesce :set echo (type -fp echo)

Nota che se tutto ciò che vuoi fare è eseguire quel echocomando, non devi ottenere il suo percorso, puoi semplicemente fare:

env echo this is not echoed by the builtin echo

Ad esempio, con tcsh, per impedire l' whichutilizzo del builtin :

set Echo = "`env which echo`"

quando hai bisogno di un comando esterno

Un altro caso in cui potresti voler usare whichè quando hai effettivamente bisogno di un comando esterno. POSIX richiede che tutti i builtin della shell (come command) siano disponibili anche come comandi esterni, ma sfortunatamente non è così per commandmolti sistemi. Ad esempio, è raro trovare un commandcomando su sistemi operativi basati su Linux mentre la maggior parte di essi ha un whichcomando (anche se diversi con opzioni e comportamenti diversi).

I casi in cui potresti desiderare un comando esterno sarebbero ovunque dovresti eseguire un comando senza invocare una shell POSIX.

Il system("some command line"), popen()... le funzioni di C o di varie lingue non invocano una shell per analizzare quella linea di comando, in modo da system("command -v my-cmd")fare il lavoro in loro. Un'eccezione sarebbe quella perlche ottimizza la shell se non vede alcun carattere speciale della shell (diverso dallo spazio). Questo vale anche per il suo operatore backtick:

$ perl -le 'print system "command -v emacs"'
-1
$ perl -le 'print system ":;command -v emacs"'
/usr/bin/emacs
0

$ perl -e 'print `command -v emacs`'
$ perl -e 'print `:;command -v emacs`'
/usr/bin/emacs

L'aggiunta di questo :;sopra costringe perla invocare un guscio lì. Usando which, non dovresti usare quel trucco.


24
@Joe, whichè una cshsceneggiatura di molti Unices commerciali. Il motivo è storico, ecco perché ho dato la storia, quindi le persone capiscono da dove viene, perché le persone si sono abituate ad usarlo e perché in realtà non c'è motivo per cui dovresti usarlo. E sì, alcune persone usano (t) csh. Non tutti usano ancora Linux
Stéphane Chazelas il

12
Dopo aver letto questo post, ho trovato molto contesto per la risposta, ma non la risposta stessa. Dove in questo post dice effettivamente perché non usare which, al contrario di cose che potresti provare whicha fare, la storia which, le implementazioni di which, altri comandi per svolgere attività correlate o ragioni per usarle which? Perché gli altri comandi sono migliori ? Cosa fanno in modo diverso da which? Come evitano le sue insidie? Questa risposta in realtà spende più parole sui problemi con le alternative che sui problemi con which.
user62251

1
Contrariamente a quanto afferma la risposta, command -vnon verifica l'autorizzazione di esecuzione, almeno se la si chiama con l'argomento nome file puro senza percorso. Ho provato con trattino 0.5.8 e GNU bash 4.3.48.
jarno,

2
@ StéphaneChazelas Se creo un nuovo file in touch /usr/bin/mytestfileseguito e successivamente eseguo command -v mytestfile, verrà indicato il percorso (mentre which mytestfilenon lo è).
jarno,

2
@jarno, oh sì, hai ragione. bashsi sistemerà su un file non eseguibile se non riesce a trovarne uno eseguibile, quindi è "OK" (anche se in pratica si preferirebbe command -v/ typerestituire un errore) in quanto questo è il comando che tenterebbe di eseguire quando si esegue mytestfile, ma il dashil comportamento è errato, come se ci fosse un non eseguibile cmdprima di un eseguibile, command -vrestituisce quello non eseguibile mentre l'esecuzione cmdeseguiva quello eseguibile (anche quello sbagliato è stato cancellato). FreeBSD sh(anch'esso basato su ash) ha lo stesso bug. zsh, yash, ksh, mksh, bash as sh sono OK.
Stéphane Chazelas

47

I motivi per cui si potrebbe non voler usare whichsono già stati spiegati, ma qui ci sono alcuni esempi su alcuni sistemi in cui whicheffettivamente fallisce.

Sulle shell tipo Bourne, stiamo confrontando l'output di whichcon l'output di type( typeessendo una shell incorporata, è pensata per essere la verità fondamentale, poiché è la shell che ci dice come invocherebbe un comando).

Molti casi sono casi angolari , ma tieni presente che which/ typesono spesso usati in casi angolari (per trovare la risposta a un comportamento inaspettato come: perché mai quel comando si sta comportando così, quale sto chiamando? ).

La maggior parte dei sistemi, la maggior parte delle shell tipo Bourne: funzioni

Il caso più ovvio riguarda le funzioni:

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

Il motivo è che whichriporta solo gli eseguibili, e talvolta sugli alias (anche se non sempre quelli della vostra shell), non funzioni.

La GNU di cui la pagina man ha un esempio non funzionante (poiché si sono dimenticati di citare $@) su come usarla anche per riportare le funzioni, ma proprio come per gli alias, poiché non implementa un parser di sintassi della shell, è facilmente ingannabile:

$ 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;
}

La maggior parte dei sistemi, la maggior parte delle shell tipo Bourne: builtin

Un altro caso ovvio sono i builtin o le parole chiave, in quanto whichessere un comando esterno non ha modo di sapere quali builtin ha la tua shell (e alcune shell come zsh, basho kshpossono caricare i builtin dinamicamente):

$ 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

(che non si applica a zshdove whichè incorporato)

Solaris 10, AIX 7.1, HP / UX 11i, Tru64 5.1 e molti altri:

$ 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

Questo perché sulla maggior parte degli Unici commerciali which(come nell'implementazione originale su 3BSD) è presente uno cshscript che legge ~/.cshrc. Gli alias che riporterà sono quelli definiti lì indipendentemente dagli alias che hai attualmente definito e indipendentemente dalla shell che stai effettivamente utilizzando.

In HP / UX o Tru64:

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

(le versioni Solaris e AIX hanno risolto il problema salvando $pathprima di leggere ~/.cshrce ripristinarlo prima di cercare i comandi)

$ 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

O:

$ 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

(ovviamente, essendo uno cshscript non puoi aspettarti che funzioni con argomenti contenenti spazi ...)

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

Su quel sistema, esiste un alias definito a livello di sistema che avvolge il whichcomando GNU .

L'uscita fasullo è perché whichsi legge l'uscita di bash's alias, ma non sa come analizzare correttamente e utilizza euristica (un alias per linea, sembra per la prima trovato un comando dopo |, ;, &...)

La cosa peggiore su CentOS è che zshha un whichcomando incorporato perfettamente perfetto ma CentOS è riuscito a romperlo sostituendolo con un alias non funzionante a GNU which.

Debian 7.0, ksh93:

(sebbene si applichi alla maggior parte dei sistemi con molte shell)

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

Su Debian, /bin/whichè una /bin/shsceneggiatura. Nel mio caso, shessere dashma è lo stesso quando lo è bash.

Un unset PATHnon è quello di disabilitare PATHricerca, ma significa utilizzando il sistema percorso predefinito che purtroppo su Debian, nessuno è d'accordo su ( dashe bashavere /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin, zshha /bin:/usr/bin:/usr/ucb:/usr/local/bin, ksh93ha /bin:/usr/bin, mkshha /usr/bin:/bin( $(getconf PATH)), execvp()(come in env) ha :/bin:/usr/bin(sì, guarda nella directory corrente prima! )).

Questo è il motivo per cui whichsi sbaglia sopra perché utilizza dashil valore predefinito PATHche è diverso da quello ksh93di

Non è meglio con GNU whichche riporta:

which: no which in ((null))

(È interessante notare che v'è infatti una /usr/local/bin/whichsul mio sistema, che è in realtà uno akangascript che è venuto con akanga(un rcderivato di shell in cui il valore di default PATHè /usr/ucb:/usr/bin:/bin:.))

bash, qualsiasi sistema:

Quello a cui si riferisce Chris nella sua risposta :

$ 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

Anche dopo aver chiamato hashmanualmente:

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

Ora un caso in cui whichea volte typefallisce:

$ 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

Ora, con alcune conchiglie:

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

Con altri:

$ foo
a/foo

which, né typepuò sapere in anticipo che b/foonon può essere eseguito. Alcune conchiglie come bash, ksho yash, quando si richiama foosarà davvero provare a correre b/fooe segnalare un errore, mentre altri (come zsh, ash, csh, Bourne, tcsh) verrà eseguito a/foosul fallimento della execve()chiamata di sistema su b/foo.


mkshutilizza effettivamente qualcosa di diverso per impostazione predefinita $PATH: in primo luogo, _PATH_DEFPATHviene utilizzata la costante di compilazione del sistema operativo (più comunemente sui BSD), quindi confstr(_CS_PATH, …)viene utilizzato (POSIX) e, se entrambi non esistono o falliscono, /bin:/usr/bin:/sbin:/usr/sbinviene utilizzato.
mirabilos,

1
Nel tuo primo esempio, anche se lsè una funzione che utilizza lsda PATH. E whichva bene dirti quale è usato /usr/bin/ls o /usr/local/bin/ls. Non vedo "Perché non usare quale" ....
Rudyier

@rudimeier, Questo which lsmi darà /bin/lsindipendentemente dal fatto che la lsfunzione chiami /bin/lso /opt/gnu/bin/lso diro niente affatto. IOW, which(ciò che le implementazioni, IMMV) sta dando qualcosa di irrilevante
Stéphane Chazelas,

1
@ StéphaneChazelas. No, no, no. Io so già che la mia lsè una funzione. Io so che la mia lsfunzione sta chiamando lsda PATH. Ora whichmi dice dove si trova il file. Si vede un solo caso d'uso: "Cosa farebbe la mia shell con questo comando". Per questo caso d'uso whichè sbagliato, corretto. Ma ci sono altri casi d'uso in cui (GNU) whichè esattamente la cosa giusta.
rudimeier,

@rudimetro, dipende whichdall'implementazione. Alcuni ti diranno che è un alias (se hai un alias configurato o se ce n'è uno ~/.cshrcnella tua casa che ha un tale alias), alcuni ti daranno un percorso ma quello sbagliato in alcune condizioni. sh -c 'command -v ls', anche se non perfetto è ancora più probabile che ti dia la risposta giusta a quel diverso requisito (ed è anche standard).
Stéphane Chazelas,

21

Una cosa che (dalla mia breve rassegna) sembra che Stephane non abbia menzionato è che whichnon ha idea della tabella di hash del percorso della shell. Ciò ha l'effetto che potrebbe restituire un risultato che non è rappresentativo di ciò che viene effettivamente eseguito, il che lo rende inefficace nel debug.


6

Nello spirito UNIX: fare in modo che ogni programma faccia bene una cosa.

Se l'obiettivo è rispondere: quale eseguibile esiste con questo nome?

Il programma eseguibile fornito con i sistemi Debian è una buona risposta. Quello fornito con csh includeva alias, che è fonte di problemi. Ciò che alcune shell forniscono come built-in interno ha un obiettivo diverso. Utilizzare tale eseguibile o utilizzare lo script fornito alla fine di questa risposta.

Se viene utilizzato questo script, ciò che risponde è pulito, semplice e utile.

Questo obiettivo corrisponderà alla prima frase della tua domanda:

Quando cerchi il percorso di un eseguibile ... ...

Se hai un sistema che non ha un eseguibile chiamato nel quale (la maggior parte dei sistemi Linux ne ha uno) puoi crearne uno in ~/bin/whichprecedenza /bin/nel PERCORSO, quindi l'eseguibile personale ha la precedenza su quelli di sistema come quello in fondo a questo post:

L'eseguibile elencherà (per impostazione predefinita) tutti gli eseguibili trovati nel PERCORSO. Se è richiesto solo il primo, l'opzione -fè disponibile.


A questo punto cadiamo in un obiettivo diverso:

cosa eseguirà la shell (dopo l'analisi)

Viene dalla tua seconda frase:

controllare cosa accadrebbe se si immette un nome di comando in una shell Unix

Questo secondo argomento cerca di trovare una buona risposta a una domanda a cui è piuttosto difficile rispondere. Le conchiglie hanno viste divergenti, casi d'angolo e (almeno) interpretazioni diverse. Aggiungendo a quello:

c'è una moltitudine di utilità diverse (quali tipo, comando, da dove, dove, dove, cosa, hash, ecc.).

E sicuramente, tutti i tentativi corrispondono a quell'obiettivo.


Evitare quale?

Sentiamo spesso ciò che dovrebbe essere evitato.

Mi chiedo: perché dovrebbe essere detto se whichfunziona bene (almeno in debian)?

Nello spirito UNIX: fai in modo che ogni programma faccia bene una cosa.

Il programma esternowhich sta facendo una cosa: trova il primo eseguibile sul PERCORSO che ha lo stesso nome del nome del comando . E lo sta facendo abbastanza bene.

Non conosco nessun altro programma o utilità che risponda a questa domanda in modo più fondamentale. Come tale, è utile e potrebbe essere utilizzato quando necessario.

L'alternativa più vicina sembra essere command -pv commandName:, ma riporterà anche su builtin e alias. Non è la stessa risposta.

Certo, whichè limitato, non risponde a tutte le domande, nessuno strumento potrebbe farlo (beh, non ancora ...). Ma è utile quando viene utilizzato per rispondere alla domanda a cui è stato progettato (quello appena sopra). Molto simile edera limitato e quindi sedappariva (o vi/ vim). O come awkera limitato e faceva apparire ed estendere Perl. Eppure, ed, sedo / e awkhanno casi d'uso specifici in cui vimo perlsono non i migliori strumenti.

Perché?

Probabilmente perché whichrisponde solo a una parte della domanda che un utente della shell potrebbe porre:

Cosa viene eseguito quando si digita un commandName?


Esterno che

Quale dovrebbe essere disponibile (in molti sistemi) come eseguibile esterno.
L'unico modo sicuro per chiamare quello strumento esterno è usare env per uscire dalla shell e quindi chiamare which(che funziona in tutte le shell):

 $ env which which
 /usr/bin/which

Oppure utilizza il percorso completo di which(che può variare su sistemi diversi):

 /usr/bin/which which 

Perché è hacknecessario? Perché alcune shell (specialmente zsh) nascondono which:

 $ zsh -c 'which which'
 which: shell built-in command

Essere uno strumento esterno (come env) spiega perfettamente perché non riporterà informazioni interne alla shell. Come alias, funzioni, builtin, builtin speciali, variabili shell (non esportate), ecc:

 $ env which ls
 /usr/bin/ls
 $ env which ll       # empty output

L'output vuoto di ll(un alias comune per ll='ls -l') indica che llnon è correlato a un programma eseguibile, o almeno, che non esiste un file eseguibile denominato llnel PERCORSO. L'uso di lldovrebbe chiamare qualcos'altro, in questo caso, un alias:

 $ type ll
 ll is aliased to `ls -l'

type e command

I comandi typee command -vsono richiesti da POSIX. Dovrebbero funzionare nella maggior parte delle conchiglie, e lo fanno, tranne in csh, tcsh, fish e rc.

Entrambi i comandi potrebbero essere utilizzati per fornire un altro punto di vista su quale comando verrà eseguito.

whence, where, whereis, whatis,hash

Poi, ci sono whence, where, whereis, whatis, hash, e alcuni altri. Tutte le diverse risposte a domande simili. Tutti funzionano in modi diversi in diverse shell. Probabilmente, whenceè il più comune dopo type. Le altre sono soluzioni speciali che rispondono alla stessa domanda in modi diversi.

Cosa dovremmo usare invece?

Probabilmente whichprima di sapere se esiste un eseguibile dal nome del CommandName , quindi typee commande poi, se il CommandName non è stato ancora trovato: whence, where, whereis, whatis, hashin questo ordine.


Script della shell per fornire un whicheseguibile.

#! /bin/sh
set -ef; oldIFS=$IFS; IFS=:

say()( IFS=" "; printf "%s\n" "$*"; )
say "Simplified version of which."
usage(){ say Usage: "$0" [-f] args; }
if [ "$#" -eq 0 ]; then say Missing argument(s); usage; exit 2; fi

firstmatch=0
while getopts f whichopts; do
    case "$whichopts" in
        f) firstmatch=1 ;;
        ?) usage; exit 3 ;;
    esac
done
[ "$OPTIND" -gt 1 ] && shift `expr "$OPTIND" - 1`

allret=0; [ "$#" -eq 0 ] && allret=1
for program in "$@"; do
    ret=1
    for element in $PATH''; do
        case "$program" in
            */*) element="$program"; loop=0;;
            *)   element="${element:-.}/$program"; loop=1;;
        esac
        if [ -f "$element" ] && [ -x "$element" ]; then
            say "$element"
            ret=0
            if [ "$firstmatch" -eq 1 ] || [ "$loop" -eq 0 ]; then break; fi
        fi
    done
    [ "$ret" -eq 1 ] && allret=1
done

IFS="$oldIFS"
exit "$allret"

0

Sentiamo spesso ciò che dovrebbe essere evitato. Perché? Cosa dovremmo usare invece?

Non l'ho mai sentito. Fornisci esempi specifici. Mi preoccuperei della tua distribuzione linux e dei pacchetti software installati, da dove whichproviene!

SLES 11.4 x86-64

nella versione 6.18.01 di tcsh:

> which which

which: shell built-in command.

nella versione bash 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.

whichfa parte di util-linux un pacchetto standard distribuito dalla Linux Kernel Organization per l'uso come parte del sistema operativo Linux. Fornisce anche questi altri file

/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

my util-linuxè la versione 2.19. Le note sulla versione sono facilmente reperibili in v2.13 del 28 agosto 2007. Non sono sicuro di quale sia stato il punto o l'obiettivo di questo, certamente non è stato risposto in quella lunga cosa votata 331 volte.


2
Nota come la domanda non fa menzione di ciò a cui Unix si riferisce. Linux è solo uno dei pochi.
Kusalananda

2
Come which -vmostra il tuo , è GNU che (quello stravagante menzionato nell'altra risposta e non è in alcun modo specifico per Linux), non util-linux che AFAIK non ha mai incluso whichun'utilità. util-linux 2.19 è del 2011, GNU, 2.19 del 2008.
Stéphane Chazelas,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.