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 which
comando è 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-cmd
comandi 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
. csh
introdusse alias intorno al 1978 (anche se csh
fu pubblicato per la prima volta nel 2BSD
maggio 1979) e anche l'elaborazione di un .cshrc
per gli utenti per personalizzare la shell (ogni shell, come si csh
legge .cshrc
anche 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 rc
file ( .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 which
script csh per gli csh
utenti per aiutare a identificare un eseguibile, ed è uno script difficilmente diverso che puoi trovare oggi which
su molti Unices commerciali (come Solaris, HP / UX, AIX o Tru64).
Tale script legge l'utente ~/.cshrc
(come fanno tutti gli csh
script se non viene invocato con csh -f
) e cerca i nomi dei comandi forniti nell'elenco degli alias e in $path
(l'array che csh
mantiene basato su $PATH
).
Ecco qui, è which
arrivato il primo per la shell più popolare al momento (ed csh
era 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 csh
utente, quello which
script 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, source
ingingendo un altro csh
file e (anche se non sarebbe una buona idea), PATH
potrebbe essere ridefinito ~/.cshrc
.
Eseguire quel which
comando 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 type
comando 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 type
comando iniziale presentava un problema simile a quello dello which
script 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/ls
invece di semplicemente /bin/ls
che lo rendeva meno facile da usare negli script.
La shell Bourne di Unix versione 8 (non rilasciata in natura) aveva il type
nome incorporato whatis
. E anche la shell Plan9 (l'ex successore di Unix) rc
(e i suoi derivati come akanga
e 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 csh
funzionalità (editor di linee, alias ...) in cima alla shell Bourne . Ha aggiunto il proprio whence
builtin (oltre a type
) che ha preso diverse opzioni ( -v
per fornire l' type
output dettagliato simile e -p
per 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 type
builtin molto tempo dopo (in NetBSD 1.3 e FreeBSD 2.3), sebbene lo fosse hash -v
. OSF / 1 /bin/sh
aveva un type
builtin che restituiva sempre 0 fino a OSF / 1 v3.x. bash
non aggiungere un whence
ma ha aggiunto -p
un'opzione per type
stampare il percorso ( type -p
sarebbe come whence -p
) e -a
di segnalare tutti i comandi corrispondenti. tcsh
reso which
incorporato e aggiunto un where
comando che agisce come quello bash
di type -a
. zsh
li ha tutti.
La fish
shell (2005) ha un type
comando implementato come funzione.
Lo which
script 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
, whereis
si comporta come which
se non fosse che cerca solo gli eseguibili $PATH
). In OpenBSD e FreeBSD, è which
stato anche cambiato in uno scritto in C che cerca solo i comandi $PATH
.
implementazioni
Esistono dozzine di implementazioni di un which
comando su vari Unices con sintassi e comportamento diversi.
Su Linux (oltre a quelli integrati in tcsh
e zsh
) troviamo diverse implementazioni. Sui recenti sistemi Debian, ad esempio, è un semplice script shell POSIX che cerca i comandi $PATH
.
busybox
ha anche un which
comando.
C'è uno GNU
which
che è probabilmente il più stravagante. Cerca di estendere ciò che lo which
script 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 bash
farlo) .
zsh
ha un paio di operatori da espandere al percorso degli eseguibili: l' operatore di =
espansione del nome file e il :c
modificatore 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/parameters
modulo crea anche il comando hash table come commands
array associativo:
$ print -r -- $commands[ls]
/bin/ls
L' whatis
utilità (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 3BSD
stesso momento come which
se fosse stato scritto C
, non csh
ed è 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 -v
e -V
(che erano opzionali fino a POSIX.2008). UNIX specifica il type
comando (nessuna opzione). Questo è tutto ( where
, which
, whence
non sono specificate in qualsiasi standard)
Fino ad alcune versioni, type
ed command -v
erano opzionali nella specifica Base standard di Linux, il che spiega perché, ad esempio, alcune vecchie versioni di posh
(sebbene basate su pdksh
quale 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 type
e command -v
sono onnipresenti in tutte le shell tipo Bourne (sebbene, come notato da @jarno, notare l'avvertenza / bug in bash
quando 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 type
c'è e which
c'è incorporato).
Nei gusci diversi tcsh
e zsh
, which
può 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
, ~/.bashrc
o qualsiasi file di avvio della shell e non si definisce $PATH
nel 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 where
in tcsh
o zsh
, type -a
in bash
o zsh
, whence -a
in ksh93 e in altre shell, puoi usarlo type
in combinazione con il which -a
quale 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 $PATH
componenti 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 $PATH
fino a quando la execve
chiamata di sistema non ritorna con un errore . Ad esempio, se $PATH
contiene /foo:/bar
e si desidera eseguire ls
, tenteranno prima di eseguire /foo/ls
o se ciò fallisce /bar/ls
. Ora esecuzione di/foo/ls
potrebbe 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 ls
segnalerebbe /foo/ls
se si dispone dell'autorizzazione di esecuzione per /foo/ls
, ma l'esecuzione ls
potrebbe effettivamente essere eseguita /bar/ls
se /foo/ls
non è un eseguibile valido.
- se
foo
è un builtin o una funzione o un alias, command -v foo
restituisce foo
. Con alcune shell come ash
, pdksh
o zsh
, può anche restituire foo
se $PATH
include la stringa vuota e c'è un foo
file 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 mount
volte è incorporato per busybox sh
) e, ad esempio, bash
può ottenere funzioni dall'ambiente.
- se
$PATH
contiene 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 cmd
potrebbe non generare un percorso assoluto. Quindi il percorso che ottieni nel momento in cui corri command -v
non sarà più valido dopo te cd
da 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 chmod
restituisce /opt/ast/bin/chmod
anche 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 csh
e tcsh
non hai molta scelta. In tcsh
, va bene come which
è incorporato. In csh
, quello sarà il which
comando 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 Bourne
script di shell, ovvero le shell che non hanno whence -p
(come ksh
o zsh
) , command -ev
(like yash
), whatis -p
( rc
, akanga
) o un builtin which
(like tcsh
o zsh
) su sistemi dove which
è disponibile e non è lo csh
script.
Se tali condizioni sono soddisfatte, allora:
echo=$(which echo)
ti darebbe il percorso del primo echo
in $PATH
(tranne nei casi d'angolo), indipendentemente dal fatto che echo
si tratti o meno di una shell builtin / alias / function.
In altre shell, preferiresti:
- zsh :
echo==echo
oppure 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 echo
comando, 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' which
utilizzo 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 command
molti sistemi. Ad esempio, è raro trovare un command
comando su sistemi operativi basati su Linux mentre la maggior parte di essi ha un which
comando (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 perl
che 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 perl
a invocare un guscio lì. Usando which
, non dovresti usare quel trucco.
which
stia assumendo un contesto di shell interattivo. Questa domanda è taggata / portabilità. Quindi interpreto la domanda in questo contesto come "cosa usare invece diwhich
trovare il primo eseguibile di un determinato nome nel$PATH
". La maggior parte delle risposte e delle ragioni contro l'which
affare 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.
).