GNU/Linux >> Linux Esercitazione >  >> Linux

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

Quando cerchi il percorso di un eseguibile o controlli cosa accadrebbe se inserisci un nome di comando in una shell Unix, c'è una pletora di utilità diverse (which , type , command , whence , where , whereis , whatis , hash , ecc).

Sentiamo spesso quel which dovrebbe essere evitato. Come mai? Cosa dovremmo usare invece?

Risposta accettata:

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

Riepilogo

Per ottenere il percorso di un eseguibile in uno script di shell simile a 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 command è un'eredità spezzata dalla C-Shell ed è meglio lasciarlo da solo in shell tipo Bourne.

Casi d'uso

C'è una distinzione tra la ricerca di tali informazioni come parte di uno script o interattivamente 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 digitato mycmd ? Posso approfondire di cosa si tratta?

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

Negli script di shell, tende ad essere abbastanza diverso. In uno script di shell non c'è motivo per cui vorresti sapere dove o cos'è un comando se tutto ciò che vuoi fare è eseguirlo. In genere, quello che vuoi sapere è il percorso dell'eseguibile, quindi puoi ottenere più informazioni da esso (come il percorso di un altro file relativo a quello, o leggere le informazioni dal contenuto del file eseguibile in quel percorso).

Interattivamente, potresti voler sapere di tutti il 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.

Cronologia

Prima un po' di storia.

Le prime shell Unix fino alla fine degli anni '70 non avevano funzioni o alias. Solo la tradizionale ricerca di eseguibili in $PATH . csh ha introdotto alias intorno al 1978 (sebbene csh è stato rilasciato per la prima volta in 2BSD , nel maggio 1979), e anche l'elaborazione di un .cshrc per gli utenti di personalizzare la shell (ogni shell, come csh , legge .cshrc anche quando non interattivo come negli script).

Sebbene la shell Bourne sia stata rilasciata per la prima volta in Unix V7 all'inizio del 1979, il supporto per le funzioni è stato aggiunto solo molto più tardi (1984 in SVR2) e comunque non ha mai avuto alcun rc file (il .profile è configurare il tuo ambiente, non la shell di per sé ).

csh divenne molto più popolare della shell Bourne poiché (sebbene avesse una sintassi terribilmente peggiore della shell Bourne) aggiungeva molte funzionalità più comode e piacevoli per l'uso interattivo.

In 3BSD (1980), un which lo script csh è stato aggiunto per csh utenti per aiutare a identificare un eseguibile, ed è uno script appena diverso che puoi trovare come which su molti Unice commerciali al giorno d'oggi (come Solaris, HP/UX, AIX o Tru64).

Quello script legge il ~/.cshrc dell'utente (come tutti i csh gli script lo fanno a meno che non vengano invocati con csh -f ), e cerca i nomi dei comandi forniti nell'elenco degli alias e in $path (l'array che csh mantiene in base a $PATH ).

Ecco a voi:which è arrivato primo per la shell più popolare dell'epoca (e csh era ancora popolare fino alla metà degli anni '90), motivo principale per cui è stato documentato nei libri ed è ancora ampiamente utilizzato.

Nota che, anche per un csh utente, quel which csh script non fornisce necessariamente le informazioni corrette. Ottiene gli alias definiti in ~/.cshrc , non quelli che potresti aver definito in seguito al prompt o ad esempio da source ing un altro csh file e (anche se non sarebbe una buona idea), PATH potrebbe essere ridefinito in ~/.cshrc .

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

Una funzionalità simile non è stata aggiunta alla shell Bourne fino al 1984 in SVR2 con il type comando integrato. Il fatto che sia integrato (al contrario di uno script esterno) significa che può darti le informazioni giuste (in una certa misura) in quanto ha accesso alle parti interne della shell.

Il type iniziale comando ha sofferto di un problema simile a which script in quanto non ha restituito uno stato di uscita non riuscito se il comando non è stato trovato. Inoltre, per gli eseguibili, contrariamente a which , produce qualcosa come ls is /bin/ls anziché solo /bin/ls che lo ha reso meno facile da usare negli script.

La shell Bourne di Unix versione 8 (non rilasciata in natura) aveva il suo type builtin rinominato in whatis ed esteso per riportare anche i parametri e le definizioni delle funzioni di stampa. Ha anche corretto type problema di non restituire un errore quando non si riesce a trovare un nome.

rc , la shell di Plan9 (il futuro successore di Unix) (e i suoi derivati ​​come akanga e es ) hanno whatis anche.

La shell Korn (un sottoinsieme di cui POSIX sh definizione è basata su), sviluppato a metà degli anni '80 ma non ampiamente disponibile prima del 1988, ha aggiunto molti dei csh funzionalità (editor di riga, alias...) sulla parte superiore della shell Bourne. Ha aggiunto il proprio whence builtin (oltre a type ) che richiedeva diverse opzioni (-v per fornire il type -come output dettagliato e -p cercare solo eseguibili (non alias/funzioni...)).

Correlati:Debian – Come utilizzare i driver wireless proprietari durante l'installazione di Debian USB?

In coincidenza con le turbolenze relative ai problemi di copyright tra AT&T e Berkeley, alcuni software gratuiti le implementazioni della shell sono uscite alla fine degli anni '80 e all'inizio degli anni '90. Tutta la shell di Almquist (ash , in sostituzione della shell Bourne nei BSD), l'implementazione di dominio pubblico di ksh (pdksh ), bash (sponsorizzato dalla FSF), zsh è uscito tra il 1989 e il 1991.

Ash, sebbene destinato a sostituire la shell Bourne, non aveva un type integrato fino a molto tempo dopo (in NetBSD 1.3 e FreeBSD 2.3), sebbene avesse hash -v . OSF/1 /bin/sh aveva un type builtin che ha sempre restituito 0 fino a OSF/1 v3.x. bash non ha aggiunto un whence ma ha aggiunto un -p opzione per type per stampare il percorso (type -p sarebbe come whence -p ) e -a per segnalare tutti i comandi corrispondenti. tcsh fatto which integrato e aggiunto un where comando che agisce come bash 's type -a . zsh li ha tutti.

Il fish shell (2005) ha un type comando implementato come una funzione.

Il which lo script csh nel frattempo è stato rimosso da NetBSD (poiché era integrato in tcsh e non è molto utile in altre shell) e la funzionalità è stata aggiunta a whereis (quando invocato come which , whereis si comporta come which tranne per il fatto che cerca solo eseguibili in $PATH ). In OpenBSD e FreeBSD, which è stato anche cambiato in uno scritto in C che cerca i comandi in $PATH solo.

Implementazioni

Ci sono dozzine di implementazioni di un which comando su vari Unice con sintassi e comportamento differenti.

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

busybox ha anche un which comando.

C'è un GNU which che è probabilmente il più stravagante. Cerca di estendere ciò che il which lo script csh ha fatto ad altre shell:puoi dirgli quali sono i tuoi alias e le tue funzioni in modo che possa darti una risposta migliore (e credo che alcune distribuzioni Linux mettano degli alias globali attorno a questo per bash per farlo).

zsh ha un paio di operatori per espandere il percorso degli eseguibili:il = Espansione nome file operatore e il :c modificatore di espansione della cronologia (qui applicato a espansione dei parametri ):

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

zsh , in zsh/parameters module rende anche la tabella hash dei comandi come commands array associativo:

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

Il whatis utility (tranne quella nella shell Bourne V8 Unix o Plan 9 rc /es ) non è realmente correlato in quanto è solo per la documentazione (greps il database whatis, che è la sinossi della pagina man).

whereis è stato aggiunto anche in 3BSD contemporaneamente a which sebbene sia stato scritto in C , non csh ed è usato per cercare contemporaneamente l'eseguibile, la pagina man e il sorgente ma non in base all'ambiente corrente. Quindi, ancora una volta, questo risponde a un'esigenza diversa.

Ora, sul fronte standard, POSIX specifica il command -v e -V comandi (che erano facoltativi fino a POSIX.2008). UNIX specifica il type comando (nessuna opzione). Questo è tutto (where , which , whence non sono specificati in nessuna norma).

Fino ad alcune versioni, type e command -v erano opzionali nella specifica Linux Standard Base, il che spiega perché, ad esempio, alcune vecchie versioni di posh (sebbene basato su pdksh che aveva entrambi) non aveva neanche. command -v è stato aggiunto anche ad alcune implementazioni della shell Bourne (come su Solaris).

Stato oggi

Lo stato al giorno d'oggi è che type e command -v sono onnipresenti in tutte le shell simili a Bourne (sebbene, come notato da @jarno, si noti l'avvertimento/bug in bash quando non in modalità POSIX o alcuni discendenti della shell Almquist di seguito nei commenti). tcsh è l'unica shell in cui vorresti usare which (poiché non esiste un type lì e which è integrato).

Nelle shell diverse da tcsh e zsh , which potrebbe dirti il ​​percorso dell'eseguibile specificato purché non ci siano alias o funzioni con lo stesso nome in nessuno dei nostri ~/.cshrc , ~/.bashrc o qualsiasi file di avvio della shell e non definisci $PATH nel tuo ~/.cshrc . Se hai un alias o una funzione definita per esso, potrebbe dirtelo o meno o dirti la cosa sbagliata.

Se vuoi conoscere tutti i comandi con un determinato nome, non c'è nulla di portatile. Useresti where in tcsh o zsh , type -a in bash o zsh , whence -a in ksh93 e in altre shell, puoi usare type in combinazione con which -a che potrebbe funzionare.

Raccomandazioni

Ottenere il percorso di 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 usano l'euristica per scoprire il percorso. Scorrono in sequenza il $PATH componenti e trova il primo file non di directory per il quale disponi 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 semplicemente nell'ordine di $PATH fino a execve la chiamata di sistema non ritorna con un errore. Ad esempio se $PATH contiene /foo:/bar e vuoi eseguire ls , proveranno prima ad eseguire /foo/ls o se fallisce /bar/ls . Ora esecuzione di /foo/ls potrebbe non riuscire perché non hai il permesso di esecuzione ma anche per molti altri motivi, come se non fosse un eseguibile valido. command -v ls riporterebbe /foo/ls se hai il permesso di esecuzione per /foo/ls , ma con ls potrebbe effettivamente eseguire /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 eseguibile foo file nella directory corrente. Ci sono alcune circostanze in cui potrebbe essere necessario tenerne conto. Tieni presente, ad esempio, che l'elenco dei builtin varia con l'implementazione della shell (ad esempio, mount a volte è integrato 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 riferisce entrambi alla directory corrente ma potrebbe 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 esegui command -v non sarà più valido dopo aver cd da qualche altra parte.
  • Aneddotico:con la shell ksh93, se /opt/ast/bin (sebbene quel percorso esatto possa variare su sistemi diversi, credo) è in te $PATH , ksh93 renderà disponibili alcuni incorporati extra (chmod , cmp , cat …), ma command -v chmod restituirà /opt/ast/bin/chmod anche se quel percorso non esiste.
Correlati:Dd vs cat:dd è ancora rilevante in questi giorni?

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 si potrebbe voler usare which

(t)csh

In csh e tcsh , non hai molta scelta. In tcsh , va bene come which è integrato. In csh , quello sarà il sistema which comando, che potrebbe non fare ciò che vuoi in alcuni casi.

Trova comandi solo in alcune shell

Un caso in cui potrebbe avere senso usare which è se vuoi conoscere il percorso di un comando, ignorando potenziali funzioni o funzioni integrate della shell in bash , csh (non tcsh ), dash o Bourne script di shell, ovvero shell che non hanno whence -p (come ksh o zsh ), command -ev (come yash ), whatis -p (rc , akanga ) o un which integrato (come tcsh o zsh ) su sistemi in cui which è disponibile e non è il csh copione.

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 capita anche che sia una shell incorporata/alias/funzione o meno.

In altre shell, preferiresti:

  • zsh :echo==echo o echo=$commands[echo] o echo=${${:-echo}:c}
  • ksh , zsh :echo=$(whence -p echo)
  • :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 che 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 il built-in which dall'uso:

set Echo = "`env which echo`"

Quando hai bisogno di un comando esterno

Un altro caso in cui potresti voler usare which è quando ne hai effettivamente necessità un comando esterno. POSIX richiede che tutti i built-in della shell (come command ) essere disponibile anche come comandi esterni, ma sfortunatamente non è il caso di command su 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 (sebbene diversi con opzioni e comportamenti diversi).

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

Il system("some command line") , popen() … le funzioni di C o di vari linguaggi invocano una shell per analizzare quella riga di comando, quindi system("command -v my-cmd") lavora in loro. Un'eccezione sarebbe 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 quel :; sopra forza perl per invocare una shell lì. Usando which , non dovresti usare quel trucco.


Linux
  1. Howto:cos'è Git e Github? Come lo uso e perché dovrebbe interessarmene?

  2. Cosa non mettere su un SSD?

  3. Perché eval dovrebbe essere evitato in Bash e cosa dovrei usare invece?

  4. cos'è il demone dbus e perché vlc ne ha bisogno

  5. Perché Bash è ovunque (nella maggior parte se non in tutte le distribuzioni Linux)?

Che cos'è la shell in Linux?

Che cos'è una macchina virtuale e perché usarla?

Che cosa sono i contenitori multi-account di Firefox? Perché e come usarlo?

Che cos'è Login Shell in Linux?

Cos'è la funzionalità della community di ONLYOFFICE e perché dovresti usarla?

Cos'è un file .sh?