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 sé ).
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.
whichstia assumendo un contesto di shell interattivo. Questa domanda è taggata / portabilità. Quindi interpreto la domanda in questo contesto come "cosa usare invece diwhichtrovare 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.).