Come posso * in modo affidabile * e * semplicemente * ottenere l'attuale nome dell'interprete della shell?


9

Sto cercando un modo semplice e affidabile per ottenere il nome della shell corrente dall'interno di uno script o di un file di origine ( non dalla riga di comando). Mi sarei aspettato di farlo, $(basename "$SHELL")ma se la mia shell di accesso è zshe ho il seguente codice in some_script.sh

this_shell=$( basename "$SHELL" )
echo "The Shell is $this_shell"
echo "The Shell is $0"

e lo eseguo con bash some_script.sh, elenca comunque zshanziché bashanche se l'interprete utilizzato è /bin/bash. Non sono sicuro del motivo per cui i progettisti di shell hanno scelto di mostrare la shell predefinita invece della shell corrente, ma sembra essere ciò con cui siamo bloccati.

Ci sono altre domande leggermente simili ( qui e qui ), ma le loro risposte sono insufficienti in molti modi.

  1. Spesso presumono che l'utente stia cercando di capire quale shell interattiva stanno usando, ma è pazzesco. So quello che sborsare Sono comandi digitando in - ho bisogno di codice che potrebbe essere eseguito ovunque per essere in grado di determinare quale shell si sta usando.
  2. Spesso danno diverse cose da provare - di nuovo come se fossi dalla riga di comando e possono solo giocherellare finché non impari a usare bash - ma ho bisogno di una sola cosa che sia affidabile in tutti i contesti.
  3. Spesso danno cose del tutto inutili dagli script, come echo $0. Ciò fallisce come mostrato nello script sopra. (Sì, funziona in una riga di comando della shell interattiva, ma perché non dovresti mai sapere quale shell stai usando?)
  4. A volte danno comandi che (nei miei test limitati) includono le informazioni corrette, come ps -p $$, ma mancano di comandi sed / awk multipiattaforma, compatibili con shell diverse per reindirizzarli per ottenere solo il nome della shell e ignorare le altre informazioni che arrivano per il giro.
  5. Includono cose che funzionano solo su un paio di conchiglie, come $BASH_VERSIONe $ZSH_VERSION. Voglio sostenere il maggior numero possibile di conchiglie, come ad esempio fish, csh, tcsh.

Come posso rilevare in modo affidabile e preciso qualsiasi shell corrente ? Sto cercando qualcosa che funzioni su più piattaforme, negli script e per quante più shell possibili e ragionevoli 1 .

AGGIORNAMENTO : Quando ho pubblicato questa domanda, mi aspettavo che ci fosse una struttura integrata nelle shell per darci queste informazioni, il che sembra non essere il caso. Dato che ora sembra inevitabile fare affidamento su qualcosa al di fuori delle shell, dovrei rendere più esplicito il fatto che sto chiedendo unasoluzione multipiattaforma (che, sebbene implicita dalle mie obiezioni ad altre risposte sopra, potrebbe essere facile perdere se si leggere attentamente la domanda).

Aggiornamento 2 Se qualcuno crede ancora che questo sia un duplicato della sola domanda di Linux perché la risposta di Stéphane non è solo per Linux, ecco le differenze tra ciò che sto chiedendo e ciò che ha fornito. (Nota che ciò che ha scritto è geniale e non lo sto bussando, ma non risolve il mio problema.)

  1. Sto cercando qualcosa di semplice e affidabile , quello
  2. può essere aggiunto a qualsiasi definizione script o funzione (che sarebbe di provenienza da una .zshrco .bash_profileo altro) in ramo.
  3. Non è possibile utilizzare il suo script come utility esterna che passa il nome dell'interprete allo script / alla funzione chiamante, poiché sarà sempre interpretato dall'interprete predefinito e lo restituirà. Ciò rende difficile o impossibile l'utilizzo per i miei scopi. Se è possibile, è ancora molto molto difficile e la soluzione per farlo funzionare non è data nella risposta. Pertanto non ha risposto alla mia domanda, quindi non è un duplicato.

Se si vuole vedere qualcosa che sarà lavorare, dare un'occhiata a ShellDetective su GitHub . Ciò potrebbe rendere più semplice vedere le differenze tra ciò che è già presente su SE e ciò che questa domanda sta cercando (ed è stata in effetti scritta per soddisfare le esigenze di questa domanda, che erano insoddisfatte in qualsiasi altro luogo).


(PS se non si può credere che ci sia un caso d'uso per questo, immaginate le funzioni che vengono di provenienza in .zshrc, .bash_profileo .profileseconda di ciò che viene utilizzato server e ciò conchiglie che ha a disposizione. Sono di origine in modo da avere alcuna linea shebang. Essi sono molto utili se possono funzionare in qualsiasi shell, ma a volte devono sapere in quale shell si trovano per sapere come comportarsi.)


1 Non mi occupo di "conchiglie" che non sono conchiglie nel senso tradizionale della parola. Non mi preoccupo delle shell che un ragazzo ha scritto per se stesso. Mi occupo solo di vere shell che si verificano effettivamente in natura e che qualcuno potrebbe presumibilmente dover usare quando si accede a un server su cui non possono semplicemente installare le shell che desiderano.


Per leggere la discussione completamente fuori tema sul fatto che Python sia una shell: vedi qui
iconoclast

2
Citando la risposta di Gilles alla domanda Qual è la differenza esatta tra un "terminale", una "shell", una "tty" e una "console"?   - "Una shell è l'interfaccia principale che gli utenti vedono quando effettuano l'accesso, il cui scopo principale è avviare altri programmi". IMO, è abbastanza per eliminare Perl e Python dalla categoria di "shell".
G-Man dice "Ripristina Monica" il

Relativamente vagamente: Script di compatibilità: Risparmia $? per l'uso in seguito . Un approccio suggerito: esegui un end-around delle differenze nelle sintassi delle diverse shell dicendo sh -c "…", quindi esegui la maggior parte del lavoro nella sintassi della shell POSIX.
G-Man dice "Ripristina Monica" il

2
Perché vuoi farlo ? Alcune shell, utilizzabili come shell di login, hanno una sintassi molto incompatibile (alcune shell sono linguaggi simili a Lisp!), Quindi non capisco perché tu voglia farlo. Se stai codificando file acquistabili, dovrai codificare diverse varianti e dire al tuo utente di scegliere quello appropriato.
Basile Starynkevitch il

1
La mia seconda domanda è un esempio di mascherare un guscio. Cosa fai se uno script viene eseguito da un eseguibile interprete il cui nome non corrisponde alla lingua interpretata dall'interprete? Se prendo una copia di csh, la rinomino fishe la utilizzo per eseguire il tuo script, vuoi fisho csh?
Gilles 'SO- smetti di essere malvagio'

Risposte:


5

Ho avuto grandi risultati con questa combinazione:

ps -p $$ | awk '$1 != "PID" {print $(NF)}'

Su Tru64 (OSF / 1), l'output presenta parentesi attorno alla shell. Tack on tr -d '()'per rimuoverli.

ps -p $$ | awk '$1 != "PID" {print $(NF)}' | tr -d '()'  

Sembra funzionare su tutte le shell, su Solaris 10 e RHEL 5.7 / 6.4. Non ho testato altre distro come Debian, Ubuntu o Mint, ma penso che dovrebbero funzionare tutti allo stesso modo; Inoltre non ho idea se FreeBSD funzionerebbe.

inserisci qui la descrizione dell'immagine


3
Non funziona negli script (restituirà invece il nome dello script)
Qw3ry

2

Quindi sto ancora cercando di chiarire questo, ma penso di avere un'idea che funzionerà. Come hai notato, ciò che stai cercando di fare è se non impossibile, estremamente difficile da fare in tutti i gusci (ogni variante che aggiungi al poliglotta aumenta la complessità ad una velocità maggiore di lineare). probabilmente potresti farlo se ti dividi in Bourne (sh, ash, dash, bash, ksh, pdksh, zsh, ecc.) e shell in stile c (csh, tcsh, fish, ecc.) ma la variazione tra le cshvarianti presenta vari interessanti sfide.

Quindi imbrogliamo e scriviamo la nostra routine di rilevamento in un linguaggio noto (bash con una riga shebang, C, perl, python, qualunque cosa) e determiniamo il nome dell'eseguibile del genitore. possiamo quindi usare due trucchi per riportare le informazioni al genitore, vale a dire il / i valore / i di ritorno / i e una sola parola scritta in base allo standard. Su sh shell discese restituiremmo 0 e scriveremo il nome della shell poiché hanno tutte una buona gestione del backtick. sulla famiglia di shell C possiamo iniziare ad incrementare il valore di ritorno per ogni serie di incompatibilità di cui abbiamo bisogno per aggirare. Valori di ritorno superiori a duecento indicano che gli errori che iniziano con tutto sembra a posto, ma non riesco proprio a capire chi è il mio genitore a duecento.

Il programma interno sembra facile su Linux ( /proc/ppid/exeè metà della battaglia). Sono abbastanza sicuro che questo possa essere fatto su bsd con le opzioni ps, ma al momento non ho un sistema bsd in esecuzione per testarlo, e il mio mac ha bisogno di un nuovo disco rigido (ed è comunque solo 10.4). Sebbene riduca significativamente i problemi di sintassi della shell, introduce una serie diversa di problemi di compatibilità. Penso ancora che abbia possibilità.


Questa è l'intuizione su cui alla fine ho deciso - che hai bisogno di un programma esterno per fare il lavoro - anche se l'ho capito leggendo il commento di G-Man sh -c "...", che ha preso da un'altra domanda . (Mi dispiace: all'inizio ho saltato la tua risposta perché non vedevo alcun codice in esso, e sono tornato a leggerlo solo dopo.)
iconoclast

C'è un paradosso nella domanda di @ iconoclast. Vuole chiamarlo da uno script (si noti che alcune shell non supportano le funzioni) che possono provenire da qualsiasi shell, quindi presumibilmente lo script è già poliglotta, quindi deve già avere un supporto per capire cosa lo sta interpretando. Nella mia esperienza, scrivere codice poliglotta (il mio which_intepreter è un esempio estremo, ma vedere anche lì per uno solo shell è estremamente difficile, e generalmente non ne vale la pena.
Stéphane Chazelas,

@ StéphaneChazelas: penso che tu abbia ragione sul fatto che in genere non vale la pena (certamente generalmente no, e forse non sempre ) ma non sono pronto a mollare ... Penso che il problema si divida in 2 problemi: il primo il nome della shell e il secondo utilizzo. Entrambi (almeno praticamente) richiedono un aiuto esterno per evitare i limiti delle shell che potresti usare. Ho risolto il primo problema con una piccola utility scritta in Crystal, e quando trovo il tempo voglio risolvere anche il secondo. A proposito, la tua soluzione è geniale, ma ovviamente molto complessa.
iconoclasta il

@iconoclas, supponendo che tu possa recuperare il nome della shell in modo affidabile (nota che la sostituzione dei comandi e la sintassi dell'assegnazione delle variabili varia tra le shell), non puoi fare cose come case $shell in sh)...(che è sintassi solo Bourne) o switch ($shell)(solo csh). test "$shell" = "sh" && do-somethingfunziona in csh/ sh/ rc/ es, ma non fish...
Stéphane Chazelas,

sì, l'ho già incontrato. La cosa peggiore è che fish non caricherà nemmeno uno script che ha una sintassi che non gli piace, quindi non puoi semplicemente inviare STDERR /dev/null. Ma ho una soluzione, che posterò una volta che tutto funzionerà.
iconoclasta il

1

prova qualcosa del genere

shell_bin=$(ps h -p $$ -o args='' | cut -f1 -d' ')
echo $shell_bin

Questo fallisce in cshe tcsh.
iconoclasta il

con una piccola modifica che funzionerà in fish, ma poi solo in fish, purtroppo: ps h -p %self -o args='' | cut -f1 -d' '. Questo crea un problema con pollo e uova ... devi sapere che devi fisheseguire la fishversione del codice ...
iconoclast

@ iconoclast- set shell_bin=`ps -p $$ -o args='' | cut -f1 -d' '`- funzionerà con CSH ma non con gli altri
DarkHeart

@DarkHeart: in realtà ciò non riesce, poiché l'assegnazione delle variabili non era il problema in csh/ tcsh, era questa parte :, ps -p $$ -o args='' | cut -f1 -d' 'che ritorna -shavanti cshe -cshavanti tcsh.
iconoclasta il

1

Credo che non puoi sempre ottenere il nome della shell corrente e penso che dovresti essere consapevole dei limiti di ciò che è possibile.

Nelle distribuzioni Linux, la maggior parte degli utenti avrebbe bash come login e shell interattiva (poiché bashè la shell predefinita sulla maggior parte delle distribuzioni). Alcuni utenti dovrebbero impostare la shell su zsh, csh(e varianti) o su fish.

(Come altri commenti e risposte spiegano, trovando in modo affidabile il guscio fuori bash, zsh, tcsh, fishsta già sfidando)

Ma alcuni utenti strani possono impostare la loro shell di login a qualcosa di completamente diverso - un interprete Lisp, scsh, es, un po 'di linguaggio di scripting à la Python o OCaml, o Perl, ecc ...- ed è la loro libertà di farlo. Probabilmente, alcune persone stanno codificando la propria shell e la usano in modo interattivo. Anche se hai trovato la loro strana shell, non sarai in grado di fare nulla di utile (quindi credo che non dovresti provare a ottenere il nome della shell).

Quindi immagino che tu stia codificando alcuni file reperibili (forse generandone uno) per configurare alcuni software. Quindi, spiega cosa stai facendo e codifica il caso comune di bash(e forse zsh& tcsh) ....


-2

utilizzare sotto a proprio rischio disclaimer

tmp=`head -n 1 $0`
SHELL_BIN=`readlink -f ${tmp#*!}`
SHELL=${SHELL_BIN##*/}

mi sta dando una riga vuota ... ma se capisco cosa stai cercando di fare è molto intelligente
iconoclasta il

Non dovrebbe darti una riga vuota, a meno che l' -fopzione in readlink, alcune implementazioni non abbiano questo interruttore
gwillie

Sono su OS X e la pagina man elenca -fcome opzione disponibile, prendendo formatcome argomento.
iconoclasta il

Ciò potrebbe funzionare in alcuni casi speciali se $ 0 è uno script (inizia con #! / ...) ma non è l'impostazione normale. Qui, la maggior parte di $ 0 punta a un file ELF.

readlinknon funziona sul mio puredarwin vm quindi non ho idea di cosa stia succedendo lì.
Gwillie,
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.