modo conforme a checkbashisms per determinare la shell corrente


18

Nel mio .profile, utilizzo il seguente codice per assicurarmi che gli alias e le funzioni relativi a Bash siano forniti solo se la shell di accesso è effettivamente Bash :

# If the current (login) shell is Bash, then
if [ "${BASH_VERSION:-}" ]; then
  # source ~/.bashrc if it exists.
  if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
  fi
fi

Attualmente sto mettendo i file di configurazione della shell, gli script e le funzioni sotto controllo della versione. Di recente ho anche avviato il processo di rimozione di Bashismi casuali dagli script di shell che non beneficiano delle funzionalità specifiche di Bash, ad esempio la sostituzione function funcname()con funcname().

Per il mio repository di file shell, ho configurato un hook pre-commit che esegue l' checkbashismsutilità dal pacchetto devscripts di Debian su ciascun shfile nel repository per garantire che non introduca inavvertitamente la sintassi specifica di Bash. Tuttavia, questo genera un errore per il mio .profile:

possible bashism in .profile line 51 ($BASH_SOMETHING):
if [ "${BASH_VERSION:-}" ]; then

Mi chiedevo se c'era un modo per verificare quale shell fosse in esecuzione che non avrebbe attivato un avviso checkbashisms.

Ho controllato l'elenco delle variabili relative alla shell elencate da POSIX nella speranza che una di esse potesse essere utilizzata per mostrare la shell corrente. Ho anche esaminato le variabili impostate in una shell Dash interattiva ma, ancora una volta, non sono riuscito a trovare un candidato adatto.

Al momento, ho escluso .profiledall'elaborazione da parte di checkbashisms; è un piccolo file, quindi non è difficile controllarlo manualmente. Tuttavia, dopo aver studiato il problema, mi piacerebbe comunque sapere se esiste un metodo conforme a POSIX per determinare quale shell è in esecuzione (o almeno un modo che non causa checkbashismserrori).


Ulteriori background / chiarimenti

Uno dei motivi per cui sto mettendo i miei file di configurazione della shell sotto il controllo della versione è configurare il mio ambiente su tutti i sistemi a cui attualmente accedo regolarmente: Cygwin, Ubuntu e CentOS (sia 5 che 7, usando Active Directory per l'utente autenticazione). Accedo spesso tramite X ambienti Windows / desktop e SSH per host remoti. Tuttavia, mi piacerebbe che questo fosse a prova di futuro e che dipendesse il meno possibile dalle dipendenze del sistema e da altri strumenti.

Ho usato checkbashismscome semplice e automatizzato controllo di integrità per la sintassi dei miei file relativi alla shell. Non è uno strumento perfetto, ad esempio, ho già applicato una patch in modo che non si lamenti dell'uso dei command -vmiei script. Durante la ricerca, ho appreso che lo scopo reale del programma è garantire la conformità con la politica Debian che, a quanto ho capito, si basa su POSIX 2004 anziché sul 2008 (o sulla sua revisione del 2013).


Quello che stai facendo è conforme a POSIX come può essere qualcosa quando l'intero punto deve essere eseguito in modo diverso su diversi ambienti conformi a POSIX. La tua carne è con checkbashisms.
Gilles 'SO- smetti di essere malvagio' il

E comunque non ho mai usato checkbashisms per verificare la portabilità della mia configurazione. Lo controllo usandolo su diversi sistemi.
Gilles 'SO- smetti di essere malvagio' il

2
E a proposito, per la cosa specifica che stai facendo qui, scrivi una .bash_profilefonte che sia .profilee (condizionatamente) .bashrc.
Gilles 'SO- smetti di essere malvagio' il

Grazie per il feedback @Gilles. Questa è una soluzione molto elegante per il problema specifico.
Anthony G - giustizia per Monica

Risposte:


16

Il tuo

# If the current (login) shell is Bash, then
if [ "${BASH_VERSION:-}" ]; then
  # source ~/.bashrc if it exists.
  if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
  fi
fi

il codice è completamente conforme a POSIX e il modo migliore per verificare la tua esecuzione bash. Ovviamente la $BASH_VERSIONvariabile è specifica per bash, ma è proprio per questo che la stai usando! Per verificare che stai correndo bash!

Si noti che $BASH_VERSIONverrà impostato se bashviene invocato come basho sh. Dopo aver affermato che sei in esecuzione bash, puoi utilizzare [ -o posix ]come indicatore che la shell è stata invocata come sh(sebbene quell'opzione sia impostata anche quando POSIXLY_CORRECT è nell'ambiente o bashviene chiamato con -o posix, o con SHELLOPTS = posix nell'ambiente. Ma in tutti questi casi, bashsi comporterà come se fosse chiamato come sh).


Un'altra variabile che potresti usare al posto di $BASH_VERSIONe che checkbashismnon sembra lamentarsi se non viene passata l' -xopzione $BASH. Questo è anche specifico di bashquello che dovresti essere in grado di usare per determinare se stai correndo basho meno.


Direi anche che non è davvero un uso corretto di checkbashisms. checkbashismsè uno strumento per aiutarti a scrivere shscript portatili (come da shspecifica nella politica Debian, un superset di POSIX), aiuta a identificare la sintassi non standard introdotta da persone che scrivono script su sistemi in cui shè presente un link simbolico bash.

A .profileè interpretato da diverse shell, molte delle quali non sono conformi a POSIX. In generale, non si utilizza shcome shell di login, ma gusci piace zsh, fisho bashcon caratteristiche interattive più avanzate.

bashe zsh, quando non viene chiamato come she quando il rispettivo file di sessione del profilo ( .bash_profile, .zprofile) non è conforme a POSIX (specialmente zsh) ma continua a essere letto .profile.

Quindi non è la sintassi POSIX che desideri, .profilema una sintassi compatibile con POSIX (per sh), bashe zshse mai dovessi usare quelle shell (forse anche Bourne come legge anche la shell Bourne .profilema non si trova comunemente nei sistemi basati su Linux ).

checkbashismsti aiuterebbe sicuramente a scoprire i bashismi ma potrebbe non indicare la sintassi POSIX che non è compatibile con zsho bash.

Qui, se si desidera utilizzare un bashcodice specifico (come il lavoro attorno a quel bashbug per cui non legge ~/.bashrcnelle shell di login interattive), un approccio migliore sarebbe quello di ~/.bash_profilefarlo (prima o dopo l'approvvigionamento ~/.profiledove si inserisce la sessione comune Inizializzazioni).


Ciò non passa a checkbashismscausa della variabile utilizzata.
Thomas Dickey,

2
@ThomasDickey, sì, ma è un caso in cui il rapporto sui checkbashisms dovrebbe essere ignorato. Tutte le altre soluzioni fornite finora sono notevolmente peggiori.
Stéphane Chazelas

@ StéphaneChazelas Dici che tutte le altre soluzioni sono peggiori. Cosa ci sarebbe di sbagliato nell'usare $0questo caso particolare?
Anthony G - giustizia per Monica

2
@AnthonyGeoghegan Questo non distingue tra bash chiamato as she qualche altra shell chiamata as sh.
Gilles 'SO- smetti di essere malvagio' il

Fornisce anche un falso positivo se dasho un'altra shell non bash è stata chiamata in modo errato argv[0] == "bash", il che è del tutto legale, se improbabile.
Kevin

17

Di solito si usa $0per questo scopo. Sul sito che hai collegato si trova:

0
   (Zero.) Expands to the name of the shell or shell script.

Così semplice! Sono diventato così abituato a usare $0il nome di uno script di shell, che ho dimenticato di dimenticare che può anche riferirsi alla shell corrente. Grazie!
Anthony G - giustizia per Monica

1
Come al solito, questa convenzione è rispettata da tutti i programmi ben educati, ma un programma dannoso può confonderti usando varie chiamate di sistema della execfamiglia perché consentono al programma chiamante di identificare il binario da eseguire separatamente dalla stringa da passare come argomento 0.
dmckee,

Questo funziona per il .profile :) carino
Rob

5

Di solito la variabile d'ambiente SHELL ti dice la shell predefinita. Non dovresti aver bisogno di generare manualmente il file .bashrc (non vero vedi aggiornamento qui sotto), bash dovrebbe farlo automaticamente assicurati solo che sia nella directory $ HOME.

L'altra opzione è fare qualcosa del tipo:

 ps -o cmd= $$

che ti dirà il comando (senza argomenti, il = senza un valore non visualizzerà le intestazioni di colonna) del processo corrente. Esempio di output:

 $ps -o cmd= $$
 bash
 $sh
 $ps -o cmd= $$
 sh

AGGIORNARE:

Sono corretto! :)

Il .bashrc non viene sempre fornito come è stato menzionato nei commenti qui sotto e /programming/415403/whats-the-difference-between-bashrc-bash-profile-and-environment

Quindi potresti spostare il tuo .bashrc in .bash_profile e vedere se funziona senza dover fare il test. Altrimenti hai il test sopra.


1
.bashrcin realtà non proviene da una shell di accesso ma .profile(se esiste) o lo .bash_profilesono. SHELLnon è specificato da POSIX e, sfortunatamente, nemmeno l' cmdopzione di formato per ps.
Anthony G - giustizia per Monica

1
Questo è quello che vorrei usare, ma è anche soggetto a manipolazione dannosa.
dmckee,

Si noti che mentre ps -o cmd= $$dovrebbe funzionare praticamente ovunque, la $SHELLvariabile è completamente irrilevante. Questa è la shell predefinita dell'utente e non ha alcuna influenza su quale shell sia attualmente in esecuzione o su quale shell stia eseguendo uno script di shell.
Terdon

2
Non proprio no. molte shell leggeranno ~/.profilequando avviate come shell di login. Quindi, per esempio, $SHELLpotresti essere cshe corri bash -l. Questo inizierà bash come shell di login, quindi leggerà ~/.profile, ma $SHELLcontinuerai a puntare a csh. Inoltre, .profilepotrebbe essere fornito esplicitamente da un altro script. Poiché il PO punta alla massima robustezza, è necessario prendere in considerazione tutti i tipi di casi. In ogni caso, $SHELLnon fornisce alcuna informazione utile sulla shell attualmente in esecuzione.
Terdon

2
FWIW, anch'io sono corretto - per SHELLnon essere specificato da POSIX: pubs.opengroup.org/onlinepubs/9699919799/basedefs/…
Anthony G - giustizia per Monica

4

La domanda richiede la shell di accesso dell'utente e la shell corrente in un modo che appaga checkbashisms. Se si intende che è la shell su cui l'utente ha effettuato l'accesso, utilizzerei quello di /etc/passwd, ad es.

MY_UID=$(id -u)
MYSHELL=$(awk -F: '$3 == '$MY_UID'{ print $7; }' </etc/passwd )

Gli utenti possono ovviamente avviare una nuova shell dopo aver effettuato l'accesso. Naturalmente, se uno è bash e l'altro no, i test delle variabili d'ambiente bash potrebbero non essere d'aiuto.

Alcuni potrebbero voler utilizzare getentpiuttosto che solo il passwdfile (ma questo non rientra nell'ambito della domanda).

Sulla base del commento su LDAP e del suggerimento per logname, questo modulo alternativo potrebbe essere utilizzato:

MY_NAME=$(logname)
MYSHELL=$(getent passwd | awk -F: '$1 ~ /^'$MY_NAME'$/ {print $7;}' )

Durante il test, ho notato che lognamenon piace il suo input reindirizzato (quindi ho lasciato l'espressione divisa). Un controllo rapido mostra getentdovrebbe funzionare sulle piattaforme menzionate (sebbene ciò avrebbe dovuto essere fornito nella domanda originale):


1
Supponendo che il database degli utenti sia in / etc / passwd (non LDAP / NIS / mysql ...) e che ci sia un solo nome utente per userid. (sarebbe meglio controllare la prima colonna contro $(logname)).
Stéphane Chazelas

iirc, POSIX non definisce l'utilità necessaria (o l'avrei usata nella mia risposta). Se usare ido una variabile modificabile dall'utente è una scelta diversa.
Thomas Dickey,

1
Si noti che ho detto di $(logname)no $LOGNAME. L'ID utente e la shell di accesso sono entrambi derivati ​​dal nome utente utilizzato al momento dell'accesso. Non è possibile tornare dall'ID utente alla shell di accesso se non sui sistemi in cui esiste un solo nome utente per ID utente. O IOW, la chiave primaria nel database utente è il nome utente, non l'UID.
Stéphane Chazelas

Grazie @ThomasDickey per una risposta proveniente da una prospettiva diversa. Tuttavia, è un po 'più complesso di quello a cui stavo pensando (più come le risposte di Jimmij e Stéphane ). Uno dei motivi per cui metto i miei file di configurazione della shell sotto il controllo della versione è configurare il mio ambiente su tutti i sistemi a cui attualmente accedo regolarmente: Cygwin, Ubuntu e CentOS (sia 5 che 7, usando Active Directory per l'utente autenticazione). Per questo motivo, preferisco mantenere la logica il più semplice possibile.
Anthony G - giustizia per Monica
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.