Perché bashrc verifica se la shell corrente è interattiva?


62

Nella mia installazione di Arch /etc/bash.bashrce /etc/skel/.bashrccontenere queste righe:

# If not running interactively, don't do anything
[[ $- != *i* ]] && return

Su Debian, /etc/bash.bashrcha:

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

E /etc/skel/.bashrc:

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

Secondo man bash, tuttavia, le shell non interattive non leggono nemmeno questi file:

   When  bash  is  started  non-interactively,  to run a shell script, for
   example, it looks for the variable BASH_ENV in the environment, expands
   its  value if it appears there, and uses the expanded value as the name
   of a file to read and execute.  Bash behaves as if the  following  com
   mand were executed:
          if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
   but  the value of the PATH variable is not used to search for the file
   name.

Se ho capito bene, i *.bashrcfile verranno letti solo se BASH_ENVè impostato per puntarli. Questo è qualcosa che non può accadere per caso e si verificherà solo se qualcuno ha impostato esplicitamente la variabile di conseguenza.

Ciò sembra spezzare la possibilità di avere automaticamente gli script che generano un utente .bashrcimpostando BASH_ENV, cosa che potrebbe tornare utile. Dato che bash non leggerà mai questi file quando viene eseguito in modo non interattivo a meno che non sia esplicitamente detto di farlo, perché i *bashrcfile predefiniti non lo consentono?


Risposte:


69

Questa è una domanda che avrei pubblicato qui qualche settimana fa. Come terdon , ho capito che a .bashrcproviene solo per shell Bash interattive, quindi non dovrebbe essere necessario .bashrcverificare se è in esecuzione in una shell interattiva. Confusamente, tutte le distribuzioni che utilizzo (Ubuntu, RHEL e Cygwin) hanno avuto un qualche tipo di controllo (test $-o $PS1) per garantire che la shell corrente fosse interattiva. Non mi piace la programmazione di culto del carico, quindi ho iniziato a capire lo scopo di questo codice nel mio .bashrc.

Bash ha un caso speciale per shell remote

Dopo aver studiato il problema, ho scoperto che le shell remote sono trattate in modo diverso. Mentre le shell Bash non interattive normalmente non eseguono ~/.bashrccomandi all'avvio, viene creato un caso speciale quando la shell viene invocata dal daemon di shell remoto :

Bash tenta di determinare quando viene eseguito con il suo input standard collegato a una connessione di rete, come quando viene eseguito dal daemon di shell remoto, in genere rshd, o dal daemon di shell sicuro sshd. Se Bash determina che viene eseguito in questo modo, legge ed esegue i comandi da ~ / .bashrc, se quel file esiste ed è leggibile. Non lo farà se invocato come sh. L' --norcopzione può essere utilizzata per inibire questo comportamento e l' --rcfileopzione può essere utilizzata per forzare la lettura di un altro file, ma né rshdné in sshdgenerale né invocare la shell con tali opzioni o consentirne la specifica.

Esempio

Inserire quanto segue all'inizio di un telecomando .bashrc. (Se .bashrcfornito da .profileo .bash_profile, disabilitalo temporaneamente durante il test):

echo bashrc
fun()
{
    echo functions work
}

Esegui i seguenti comandi localmente:

$ ssh remote_host 'echo $- $0'
bashrc
hBc bash
  • No iin $-indica che la shell non è interattiva .
  • Nessun leader -nel $0indica che la shell non è una shell di login .

Le funzioni della shell definite nel telecomando .bashrcpossono anche essere eseguite:

$ ssh remote_host fun
bashrc
functions work

Ho notato che ~/.bashrcviene fornito solo quando viene specificato un comando come argomento per ssh. Questo ha senso: quando sshviene utilizzato per avviare una normale shell di accesso .profileo .bash_profileviene eseguito (e .bashrcviene fornito solo se esplicitamente fatto da uno di questi file).

Il vantaggio principale che posso vedere per aver .bashrcottenuto quando si esegue un comando remoto (non interattivo) è che le funzioni della shell possono essere eseguite. Tuttavia, la maggior parte dei comandi in un tipico .bashrcsono rilevanti solo in una shell interattiva, ad esempio gli alias non vengono espansi a meno che la shell non sia interattiva.

I trasferimenti di file remoti possono fallire

Questo di solito non è un problema quando rsho sshvengono utilizzati per avviare una shell di accesso interattivo o quando vengono utilizzate shell non interattive per eseguire comandi. Tuttavia, può essere un problema per i programmi come rcp, scpe sftpche utilizzano shell remote per trasferire dati.

Si scopre che la shell predefinita dell'utente remoto (come Bash) viene avviata implicitamente quando si utilizza il scpcomando. Non c'è menzione di questo nella pagina man - solo una menzione che scpusa sshper il suo trasferimento di dati. Ciò ha la conseguenza che se .bashrccontiene comandi che stampano sull'output standard, i trasferimenti di file falliranno , ad esempio scp fallirà senza errori .

Vedi anche questo relativo rapporto sui bug di Red Hat di 15 anni fa, scp si interrompe quando c'è un comando echo in / etc / bashrc (che alla fine è stato chiuso come WONTFIX).

Perché scpe sftpfallire

SCP (Secure copy) e SFTP (Secure File Transfer Protocol) hanno i propri protocolli per le estremità locali e remote per scambiare informazioni sui file che vengono trasferiti. Qualsiasi testo imprevisto dall'estremità remota viene (erroneamente) interpretato come parte del protocollo e il trasferimento non riesce. Secondo una FAQ dal Snail Book

Ciò che spesso accade, però, è che ci sono dichiarazioni in entrambi il sistema o file di avvio della shell per utente sul server ( .bashrc, .profile, /etc/csh.cshrc, .login, ecc), che in uscita messaggi di testo sul login, deve essere letto dagli esseri umani (come fortune, echo "Hi there!", eccetera.).

Tale codice dovrebbe produrre output solo su accessi interattivi, quando è presente un ttyinput standard. Se non esegue questo test, inserirà questi messaggi di testo in cui non appartengono: in questo caso, inquinando il flusso di protocollo tra scp2/ sftpe sftp-server.

Il motivo per cui i file di avvio della shell sono rilevanti è che sshd utilizza la shell dell'utente all'avvio di qualsiasi programma per conto dell'utente (utilizzando ad esempio / bin / sh -c "comando"). Questa è una tradizione Unix e presenta vantaggi:

  • Le normali impostazioni dell'utente (alias dei comandi, variabili di ambiente, umask, ecc.) Sono attive quando vengono eseguiti i comandi remoti.
  • La pratica comune di impostare la shell di un account su / bin / false per disabilitarlo impedirà al proprietario di eseguire qualsiasi comando, nel caso in cui l'autenticazione dovesse continuare accidentalmente per qualche motivo.

Dettagli del protocollo SCP

Per coloro che sono interessati ai dettagli su come funziona SCP, ho trovato informazioni interessanti in Come funziona il protocollo SCP che include dettagli su Esecuzione di scp con profili di shell loquace sul lato remoto? :

Ad esempio, ciò può accadere se lo aggiungi al tuo profilo shell sul sistema remoto:

eco ""

Perché si blocca e basta? Ciò deriva dal modo in cui scpin modalità sorgente attende la conferma del primo messaggio di protocollo. Se non è 0 binario, si aspetta che sia una notifica di un problema remoto e attende che più caratteri formino un messaggio di errore fino all'arrivo della nuova riga. Dal momento che non hai stampato un'altra nuova riga dopo la prima, il tuo locale scprimane in loop, bloccato read(2). Nel frattempo, dopo l'elaborazione del profilo della shell sul lato remoto, è scpstata avviata la modalità sink, che si blocca anche in read(2)attesa di uno zero binario che indica l'inizio del trasferimento dei dati.

Conclusione / TLDR

La maggior parte delle istruzioni in un tipico .bashrcsono utili solo per una shell interattiva, non quando si eseguono comandi remoti con rsho ssh. Nella maggior parte di tali situazioni, l'impostazione di variabili di shell, alias e definizione di funzioni non è desiderata e la stampa di qualsiasi testo su standard out è attivamente dannosa se il trasferimento di file mediante programmi come scpo sftp. Uscire dopo aver verificato che la shell corrente non sia interattiva è il comportamento più sicuro per .bashrc.


bell'articolo. ma dovrei farlo? devo fare il check-in con .bashrc ma scp ancora esce con il codice 0 e non copio nulla nella casella remota
ses

@ses Sarebbe meglio porre una nuova domanda in cui è possibile descrivere la propria situazione in modo più dettagliato.
Anthony G - giustizia per Monica,

16

La pagina man trascura di menzionare che bashanche fonti .bashrcper shell remote non interattive, come in

ssh hostname command

http://git.savannah.gnu.org/cgit/bash.git/tree/shell.c#n1010

 COMMAND        EXECUTE BASHRC
 --------------------------------
 bash -c foo        NO
 bash foo           NO
 foo                NO
 rsh machine ls     YES (for rsh, which calls 'bash -c')
 rsh machine foo    YES (for shell started by rsh) NO (for foo!)
 echo ls | bash     NO
 login              NO
 bash               YES

http://git.savannah.gnu.org/cgit/bash.git/tree/shell.c#n1050

          /* If we were run by sshd or we think we were run by rshd, execute
             ~/.bashrc if we are a top-level shell. */
          if ((run_by_ssh || isnetconn (fileno (stdin))) && shell_level < 2)
            {
              maybe_execute_file (SYS_BASHRC, 1);
              maybe_execute_file (bashrc_file, 1);

Provi il tuo .bashrc dal tuo .bash_profile (o .profile)?
Glenn Jackman,

Sì, ma non è questo il punto. Un vuoto .bash_profileavrebbe mostrato lo stesso comportamento.
Mikel,

Non sono sicuro di ciò che mi sta mostrando. Stai dicendo che rsh(che non ho mai usato) fonti ~/.bashrc? E che, per estensione, le distro Linux lo bloccano bashnel caso in cui qualcuno provasse mai a eseguire uno script rsh? ssh servernon dovrebbe toccare .bashrcpoiché si tratta comunque di una shell di login. A meno che il tuo sistema non sia uno di quelli da cui proviene ~ / .bashrc` ~/.profile. E ssh server commandnon dovrei nemmeno farlo. Certamente no sul mio sistema.
terdon

2
No. Ti ho collegato alla bashfonte, non alla rshfonte. :) Il commento vale anche per ssh, è stato scritto molto tempo fa. Vedi la risposta aggiornata con più fonte.
Mikel,

Ah, è utile, grazie. Come lo testerei comunque? Ho provato ad aggiungere un echoall'inizio /etc/bash.bashrce ~/.bashrc(prima dei test interattivi della shell) e poi ssh nella macchina target con ssh server hostnamee mentre hostnameera in esecuzione, non ho visto nessuno dei miei echi. È che il mio shell_level(qualunque cosa sia) non lo è <2?
terdon

5

Per convenzione, .bashrcè il luogo in cui l'utente memorizza la configurazione personalizzata per la shell.

Queste configurazioni personalizzate possono essere variabili di ambiente, alias, prompt di fantasia. Con una shell non interattiva, quelle poche cose sono insignificanti. Inoltre, una shell non interattiva può essere chiamata in molti contesti, non sei sicuro che queste variabili di ambiente possano portare a casi falsi negativi o persino vulnerabili alla sicurezza.

Un esempio più vicino è un alias come:

alias cp='cp -i'

Quindi appende la tua shell non interattiva per sempre.

Quindi il controllo viene eseguito nella parte superiore .bashrcper garantire che non ci siano problemi.


Poiché la shell può essere chiamata come shell di login non interattiva , non è quindi necessario bloccare esplicitamente il sourcing *bashrc.

Quando il guscio è stato chiamato come login shell non interattiva , è fonte /etc/profile, quindi fonte il primo trovato in ~/.bash_profile, ~/.bash_logine ~/.profile:

Quando bash viene invocato come shell di login interattiva o come shell non interattiva con l'opzione --login , legge prima ed esegue i comandi dal file / etc / profile, se quel file esiste. Dopo aver letto quel file, cerca ~ / .bash_profile, ~ / .bash_login e ~ / .profile, in quell'ordine, e legge ed esegue i comandi dal primo che esiste ed è leggibile.

Nulla impedisce a questi file di procurarsi da .bashrcsoli, quindi fare il controllo all'interno .bashrcè più sicuro e semplifica le cose.


Sì, lo so che. Ecco perché le shell non interattive non provengono affatto dai *bashrcfile. La mia domanda è: perché vari fornitori di Linux si preoccupano di bloccare esplicitamente le shell non interattive dalla lettura di questi file se questi file non vengono comunque letti dalle shell non interattive.
terdon

1
@terdon: poiché una shell può essere chiamata come shell di accesso non interattiva , allora verrà generata una shell di accesso .bash_profile, che può essere sorgente .bashrc.
cuonglm

No, nelle shell non interattive, l'unica cosa che viene letta è $BASH_ENV. Entrambi *profilee *bashrcvengono ignorati. O, almeno, è quello che dice la pagina man. Tuttavia, come ha dimostrato Mikel, la pagina man potrebbe mentire.
terdon

@terdon: leggi la mia risposta aggiornata con il link in essa contenuto. Hai provato bash -l -c :a invocare una shell di accesso non interattiva?
cuonglm

Wow. Hai assolutamente ragione. Ho letto quella parte circa --login100 volte ma, a quanto pare, non si era mai registrato. Grazie!
terdon
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.