Sulla mia installazione di Arch, /etc/bash.bashrc
e /etc/skel/.bashrc
contengono queste righe:
# If not running interactively, don't do anything
[[ $- != *i* ]] && return
Su Debian, /etc/bash.bashrc
ha:
# 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, il *.bashrc
i file verranno letti solo se BASH_ENV
è impostato per puntare a loro. Questo è qualcosa che non può accadere per caso e accadrà solo se qualcuno ha impostato esplicitamente la variabile di conseguenza.
Ciò sembra interrompere la possibilità di avere script come fonte di .bashrc
di un utente automaticamente impostando BASH_ENV
, qualcosa che potrebbe tornare utile. Dato che bash non leggerà mai questi file quando viene eseguito in modo non interattivo a meno che non venga esplicitamente detto di farlo, perché l'impostazione predefinita *bashrc
i file non lo consentono?
Risposta accettata:
Questa è una domanda che avrei postato qui qualche settimana fa. Come terdon , ho capito che un .bashrc
viene fornito solo per shell Bash interattive, quindi non dovrebbe essere necessario .bashrc
per verificare se è in esecuzione in una shell interattiva. Confusamente, tutti le distribuzioni che uso (Ubuntu, RHEL e Cygwin) avevano un qualche tipo di controllo (test $-
o $PS1
) per garantire che la shell corrente sia interattiva. Non mi piace la programmazione cargo cult, quindi ho cercato di capire lo scopo di questo codice nel mio .bashrc
.
Bash ha una custodia speciale per le shell remote
Dopo aver studiato il problema, ho scoperto che shell remote vengono trattati in modo diverso. Mentre le shell Bash non interattive normalmente non eseguono ~/.bashrc
comandi all'avvio, si verifica un caso speciale quando la shell viene richiamata dal demone della shell remota:
Bash tenta di determinare quando viene eseguito con il suo input standard
connesso a una connessione di rete, come quando viene eseguito dal demone della shell remota
, di solito rshd
o il demone della shell sicura 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
. Il --norc
l'opzione può essere usata per inibire questo comportamento,
e il --rcfile
l'opzione può essere utilizzata per forzare la lettura di un altro file, ma
né rshd
né sshd
generalmente richiama la shell con queste opzioni o
consenti che vengano specificate.
Esempio
Inserisci quanto segue all'inizio di un .bashrc
remoto . (Se .bashrc
proviene da .profile
o .bash_profile
, disabilitalo temporaneamente durante il test):
echo bashrc
fun()
{
echo functions work
}
Esegui i seguenti comandi in locale:
$ ssh remote_host 'echo $- $0'
bashrc
hBc bash
- Nessun
i
in$-
indica che la shell è non interattiva . - Nessun
-
iniziale in$0
indica che la shell non è una shell di accesso .
Funzioni della shell definite nel .bashrc
remoto può anche essere eseguito:
$ ssh remote_host fun
bashrc
functions work
Ho notato che il ~/.bashrc
è solo originato quando un comando viene specificato come argomento per ssh
. Questo ha senso:quando ssh
viene utilizzato per avviare una normale shell di accesso, .profile
o .bash_profile
vengono eseguiti (e .bashrc
viene originato solo se esplicitamente fatto da uno di questi file).
Il vantaggio principale che posso vedere nell'avere .bashrc
originato durante l'esecuzione di un comando remoto (non interattivo) è che le funzioni della shell possono essere eseguite. Tuttavia, la maggior parte dei comandi in un tipico .bashrc
sono 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 non riuscire
Questo di solito non è un problema quando rsh
o ssh
vengono utilizzati per avviare una shell di accesso interattiva o quando vengono utilizzate shell non interattive per eseguire comandi. Tuttavia, può essere un problema per programmi come rcp
, scp
e sftp
che utilizzano shell remote per il trasferimento dei dati.
Si scopre che la shell predefinita dell'utente remoto (come Bash) viene avviata implicitamente quando si utilizza scp
comando. Non c'è alcuna menzione di questo nella pagina man, solo una menzione che scp
usa ssh
per il suo trasferimento di dati. Questo ha la conseguenza che se il .bashrc
contiene tutti i comandi che stampano sullo standard output, i trasferimenti di file non andranno a buon fine , ad esempio, scp ha esito negativo senza errori.
Perché scp
e sftp
fallire
SCP (Secure copy) e SFTP (Secure File Transfer Protocol) hanno i propri protocolli per le estremità locali e remote per lo scambio di informazioni sui file trasferiti. Qualsiasi testo imprevisto dall'estremità remota viene interpretato (erroneamente) come parte del protocollo e il trasferimento non riesce. Secondo una FAQ dello Snail Book
Ciò che accade spesso, tuttavia, è che ci sono istruzioni nel sistema
o nei file di avvio della shell per utente sul server (.bashrc
, .profile
, /etc/csh.cshrc
, .login
, ecc.) che generano messaggi di testo all'accesso,
destinati ad essere letti dagli esseri umani (come fortune
, echo "Hi there!"
, ecc.).
Tale codice dovrebbe produrre output solo su accessi interattivi, quando è presente un tty
allegato allo standard input. Se non effettua questo test,
inserirà questi sms dove non appartengono:in questo caso, inquinando
il flusso di protocollo tra scp2
/sftp
e sftp-server
.
Il motivo per cui i file di avvio della shell sono rilevanti è che sshd
utilizza la shell dell'utente quando avvia programmi per conto dell'utente (usando ad esempio /bin/sh -c "comando"). Questa è una tradizione Unix e presenta
vantaggi:
- La configurazione abituale dell'utente (alias dei comandi, variabili di ambiente, umask,
ecc.) è attiva quando vengono eseguiti i comandi remoti. - La pratica comune di impostare la shell di un account su /bin/false per disabilitare
impedirà al proprietario di eseguire qualsiasi comando, nel caso in cui l'autenticazione
dovesse ancora riuscire 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 Eseguire scp con profili di shell loquaci sul lato remoto? :
Ad esempio, ciò può accadere se lo aggiungi al tuo profilo shell sul
sistema remoto:
eco “”
Perché si blocca? Questo deriva dal modo in cui scp
nella fonte mode
attende la conferma del primo messaggio di protocollo. Se non è binario
0, 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. Poiché
non hai stampato un'altra nuova riga dopo la prima, il tuo scp
locale solo
rimane in loop, bloccato su read(2)
. Nel frattempo, dopo che il profilo della shell
è stato elaborato sul lato remoto, scp
in modalità sink è stato avviato,
che si blocca anche su read(2)
, in attesa di uno zero binario che denoti l'inizio
del trasferimento dei dati.
Conclusione/TLDR
La maggior parte delle istruzioni in un tipico .bashrc
sono utili solo per una shell interattiva, non quando si eseguono comandi remoti con rsh
o ssh
. Nella maggior parte di queste situazioni, non si desidera impostare variabili shell, alias e definire funzioni e stampare qualsiasi testo standard out è attivamente dannoso se si trasferiscono file utilizzando programmi come scp
o sftp
. Uscire dopo aver verificato che la shell corrente non sia interattiva è il comportamento più sicuro per .bashrc
.