En mi instalación de Arch, /etc/bash.bashrc
y /etc/skel/.bashrc
contener estas líneas:
# If not running interactively, don't do anything
[[ $- != *i* ]] && return
En Debian, /etc/bash.bashrc
tiene:
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
Y /etc/skel/.bashrc
:
# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac
Según man bash
, sin embargo, los shells no interactivos ni siquiera leen estos archivos:
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.
Si entiendo correctamente, el *.bashrc
los archivos solo se leerán si BASH_ENV
está configurado para señalarlos. Esto es algo que no puede suceder por casualidad y solo ocurrirá si alguien ha establecido explícitamente la variable en consecuencia.
Eso parece romper la posibilidad de que los scripts generen el .bashrc
de un usuario. automáticamente configurando BASH_ENV
, algo que podría venir muy bien. Dado que bash nunca leerá estos archivos cuando se ejecute de forma no interactiva a menos que se le indique explícitamente que lo haga, ¿por qué hacer el *bashrc
predeterminado? ¿Los archivos no lo permiten?
Respuesta aceptada:
Esta es una pregunta que iba a publicar aquí hace unas semanas. Me gusta terdón , entendí que un .bashrc
solo se obtiene para shells Bash interactivos, por lo que no debería haber necesidad de .bashrc
para comprobar si se está ejecutando en un shell interactivo. Confusamente, todos las distribuciones que uso (Ubuntu, RHEL y Cygwin) tenían algún tipo de verificación (probando $-
o $PS1
) para garantizar que el shell actual sea interactivo. No me gusta la programación de culto de carga, así que me puse a entender el propósito de este código en mi .bashrc
.
Bash tiene un caso especial para shells remotos
Después de investigar el problema, descubrí que shells remotos son tratados de manera diferente. Si bien los shells Bash no interactivos normalmente no ejecutan ~/.bashrc
comandos al inicio, se crea un caso especial cuando el shell es invocado por un daemon de shell remoto:
Bash intenta determinar cuándo se está ejecutando con su entrada estándar
conectada a una conexión de red, como cuando lo ejecuta el shell remoto
daemon, generalmente rshd
, o el daemon de shell seguro sshd
. Si Bash
determina que se está ejecutando de esta manera, lee y ejecuta comandos
desde ~/.bashrc, si ese archivo existe y se puede leer. No hará esto si
se invoca como sh
. El --norc
puede usarse para inhibir este comportamiento,
y el --rcfile
La opción puede usarse para forzar la lectura de otro archivo, pero
ni rshd
ni sshd
generalmente invoque el shell con esas opciones o
permita que se especifiquen.
Ejemplo
Inserte lo siguiente al comienzo de un .bashrc
remoto . (Si .bashrc
proviene de .profile
o .bash_profile
, desactívelo temporalmente durante la prueba):
echo bashrc
fun()
{
echo functions work
}
Ejecute los siguientes comandos localmente:
$ ssh remote_host 'echo $- $0'
bashrc
hBc bash
- No
i
en$-
indica que el shell es no interactivo . - Sin
-
iniciales en$0
indica que el shell no es un shell de inicio de sesión .
Funciones de shell definidas en el .bashrc
remoto también se puede ejecutar:
$ ssh remote_host fun
bashrc
functions work
Noté que ~/.bashrc
es solo se obtiene cuando se especifica un comando como argumento para ssh
. Esto tiene sentido:cuando ssh
se utiliza para iniciar un shell de inicio de sesión normal, .profile
o .bash_profile
se ejecutan (y .bashrc
solo se obtiene si uno de estos archivos lo hace explícitamente).
El principal beneficio que puedo ver al tener .bashrc
originado cuando se ejecuta un comando remoto (no interactivo) es que se pueden ejecutar funciones de shell. Sin embargo, la mayoría de los comandos en un típico .bashrc
solo son relevantes en un shell interactivo, por ejemplo, los alias no se expanden a menos que el shell sea interactivo.
Las transferencias remotas de archivos pueden fallar
Esto no suele ser un problema cuando rsh
o ssh
se utilizan para iniciar un shell de inicio de sesión interactivo o cuando se utilizan shells no interactivos para ejecutar comandos. Sin embargo, puede ser un problema para programas como rcp
, scp
y sftp
que usan shells remotos para transferir datos.
Resulta que el shell predeterminado del usuario remoto (como Bash) se inicia implícitamente cuando se usa el scp
dominio. No se menciona esto en la página de manual, solo se menciona que scp
usa ssh
para su transferencia de datos. Esto tiene como consecuencia que si el .bashrc
contiene cualquier comando que imprima en salida estándar, las transferencias de archivos fallarán , por ejemplo, scp falla sin error.
Por qué scp
y sftp
falla
SCP (Copia segura) y SFTP (Protocolo seguro de transferencia de archivos) tienen sus propios protocolos para que los extremos local y remoto intercambien información sobre los archivos que se transfieren. Cualquier texto inesperado del extremo remoto se interpreta (erróneamente) como parte del protocolo y la transferencia falla. Según una pregunta frecuente del Snail Book
Sin embargo, lo que sucede a menudo es que hay declaraciones en el sistema
o en los archivos de inicio de shell de cada usuario en el servidor (.bashrc
, .profile
, /etc/csh.cshrc
, .login
, etc.) que generan mensajes de texto al iniciar sesión,
destinados a ser leídos por humanos (como fortune
, echo "Hi there!"
, etc.).
Dicho código solo debe producir resultados en inicios de sesión interactivos, cuando hay un tty
conectado a la entrada estándar. Si no realiza esta prueba,
insertará estos mensajes de texto donde no pertenecen:en este caso, contaminando
el flujo de protocolo entre scp2
/sftp
y sftp-server
.
La razón por la que los archivos de inicio de shell son relevantes es que sshd
emplea el shell del usuario al iniciar cualquier programa en nombre del usuario (usando, por ejemplo, /bin/sh -c “comando”). Esta es una tradición de Unix y tiene
ventajas:
- La configuración habitual del usuario (alias de comando, variables de entorno, umask,
etc.) está en vigor cuando se ejecutan los comandos remotos. - La práctica común de establecer el shell de una cuenta en /bin/false para deshabilitar
evitará que el propietario ejecute cualquier comando, en caso de que la autenticación
aún tenga éxito accidentalmente por algún motivo.
Detalles del protocolo SCP
Para aquellos interesados en los detalles de cómo funciona SCP, encontré información interesante en Cómo funciona el protocolo SCP, que incluye detalles sobre ¿Ejecutar scp con perfiles de shell hablador en el lado remoto? :
Por ejemplo, esto puede suceder si agrega esto a su perfil de shell en el
sistema remoto:
eco “”
¿Por qué simplemente se cuelga? Eso viene de la forma en que scp
en fuente mode
espera la confirmación del primer mensaje de protocolo. Si no es binario
0, espera que sea una notificación de un problema remoto y espera
más caracteres para formar un mensaje de error hasta que llega la nueva línea. Dado que
no imprimió otra línea nueva después de la primera, su scp
local simplemente
permanece en un bucle, bloqueado en read(2)
. Mientras tanto, después de que el perfil de shell
se procesara en el lado remoto, scp
en modo sumidero,
que también bloquea en read(2)
, esperando un cero binario que indique el inicio
de la transferencia de datos.
Conclusión/TLDR
La mayoría de las declaraciones en un típico .bashrc
solo son útiles para un shell interactivo, no cuando se ejecutan comandos remotos con rsh
o ssh
. En la mayoría de estas situaciones, no se desea establecer variables de shell, alias y definir funciones, e imprimir cualquier texto estandarizar es activamente dañino si se transfieren archivos usando programas como scp
o sftp
. Salir después de verificar que el shell actual no es interactivo es el comportamiento más seguro para .bashrc
.