Propósito de [-n “$ PS1”] en bashrc

10

¿Para qué sirve el [ -n "$PS1" ]en [ -n "$PS1" ] && source ~/.bash_profile;? Esta línea se incluye en un repositorio de.bashrc archivos de puntos .

shotes
fuente

Respuestas:

20

Esto verifica si el shell es interactivo o no. En este caso, solo obtiene el ~/.bash_profilearchivo si el shell es interactivo.

Consulte "¿Es este Shell interactivo?" en el manual de bash, que cita ese idioma específico. (También recomienda verificar si el shell es interactivo probando si la $-variable especial contiene el icarácter, que es un mejor enfoque para este problema).

filbranden
fuente
bash, al menos, desarmará PS1 y PS2 si el shell es interactivo . Puedes ver eso por ti mismo, con ( export PS1='abc$ '; bash -c 'echo "[$PS1]"' ), que simplemente imprime []. Parece que zsh no hace lo mismo, al menos desde un experimento ... En cualquier caso, la intención del [ -n "$PS1" ]es verificar si el shell es interactivo o no.
filbranden
3
Eso bashdeshabilita PS1 cuando no es interactivo (error tipográfico en su comentario anterior) es un error de la OMI, PS1 no es una variable específica de bash, no tiene nada que desarmarlo. Es el único shell que hace eso (aunque yashtambién se establece PS1en un valor predeterminado incluso cuando no es interactivo).
Stéphane Chazelas
1
Dado que el código en cuestión está en un archivo específico de bash, esto parece una respuesta razonable. Otras respuestas abordan el caso más general de las especificaciones POSIX u otros shells. Has respondido "¿Cuál es el propósito de esto?" intención en la pregunta, dejando de lado apropiadamente el resto. Es bueno saber qué está haciendo bash y posiblemente mejores formas de lograr el objetivo también.
Jeff Schaller
Consideraría esta respuesta más completa si sugiriera una alternativa más confiable (por ejemplo, [[ $- = *i* ]] && source ~/.bash_profile).
Charles Duffy
@CharlesDuffy Francamente, no creo que haya mucho problema [ -n "${PS1}" ], pero todavía he actualizado mi respuesta para resaltar que el manual de bash también sugiere / recomienda inspeccionar $-para determinar si el shell es interactivo, espero que encuentre que mejora la respuesta. ¡Salud!
filbranden
19

Que hace esto

Esta es una forma generalizada de probar si el shell es interactivo. Tenga en cuenta que solo funciona en bash, no funciona con otros shells. Por lo tanto, está bien (si es tonto) .bashrc, pero no funcionaría .profile(lo que lee sh, y bash es solo una de las implementaciones posibles de sh, y no la más común).

Por qué funciona (¡solo en bash!)

Un shell interactivo establece la variablePS1 del shell en la cadena de solicitud predeterminada. Entonces, si el shell es interactivo, PS1está configurado (a menos que el usuario lo .bashrchaya eliminado, lo que no puede haber sucedido todavía en la parte superior .bashrc, y podría considerar que es algo tonto de todos modos).

Lo contrario es cierto en bash: las instancias no interactivas de bash se desarman PS1cuando comienzan. Tenga en cuenta que este comportamiento es específico de bash, y podría decirse que es un error (¿por qué bash -c '… do stuff with $var…'no funcionaría cuando lo vares PS1?). Pero todas las versiones de bash hasta 4.4 (incluida la última versión que escribo) hacen esto.

Muchos sistemas exportan PS1al medio ambiente. Es una mala idea, porque se usan muchos shells diferentes PS1pero con una sintaxis diferente (por ejemplo, los escapes de prompt de bash son completamente diferentes de los escapes de prompt de zsh ). Pero está lo suficientemente extendido como para que, en la práctica, ver que PS1esté configurado no sea un indicador confiable de que el shell sea interactivo. El shell podría haber heredado PS1del entorno.

Por qué se usa (mal) aquí

.bashrces el archivo que bash lee en el inicio cuando es interactivo. Un hecho menos conocido es que bash también lee .bashrces un shell de inicio de sesión y la heurística de bash concluye que se trata de una sesión remota (bash comprueba si su padre es rshdo sshd). En este segundo caso, es poco probable que PS1se establezca en el entorno, porque todavía no se ha ejecutado ningún archivo de puntos.

Sin embargo, la forma en que el código usa esta información es contraproducente.

  • Si el shell es un shell interactivo, esto se ejecuta .bash_profileen ese shell. Pero .bash_profilees un script de tiempo de inicio de sesión. Puede ejecutar algunos programas que están destinados a ejecutarse solo una vez por sesión. Podría anular algunas variables de entorno que el usuario había establecido deliberadamente en un valor diferente antes de ejecutar ese shell. Ejecutar .bash_profileen un shell sin inicio de sesión es perjudicial.
  • Si el shell es un shell de inicio de sesión remoto no interactivo, no se cargará .bash_profile. Pero este es el caso en el que la carga .bash_profilepodría ser útil, ya que un intérprete de ingreso no interactivo no se carga automáticamente /etc/profiley ~/.profile.

Creo que la razón por la que las personas hacen esto es para los usuarios que inician sesión a través de una GUI (un caso muy común) y que establecen su configuración de variables de entorno en .bash_profilelugar de hacerlo .profile. La mayoría de los mecanismos de inicio de sesión de la GUI invocan .profilepero no .bash_profile(la lectura .bash_profilerequeriría ejecutar bash como parte del inicio de sesión, en lugar de sh). Con esta configuración, cuando el usuario abre un terminal, obtendrá sus variables de entorno. Sin embargo, el usuario no obtendrá sus variables de entorno en las aplicaciones GUI, lo cual es una fuente muy común de confusión. La solución aquí es usar en .profilelugar de .bash_profileestablecer variables de entorno. Agregar un puente entre .bashrcy .bash_profilecrea más problemas de los que resuelve.

Que hacer en su lugar

Hay una forma sencilla y portátil de probar si el shell actual es interactivo: pruebe si la opción -iestá habilitada.

case $- in
  *i*) echo "This shell is interactive";;
  *) echo "This shell is not interactive";;
esac

Esto es útil .bashrcpara leer .profilesolo si el shell no es interactivo , es decir, lo contrario de lo que hace el código. Lea .profilesi bash es un shell de inicio de sesión (no interactivo) y no lo lea si es un shell interactivo.

if [[ $- != *i* && -r ~/.profile ]]; then . ~/.profile; fi
Gilles 'SO- deja de ser malvado'
fuente
44
Vale la pena señalar que una mejor manera de probar si un shell es interactivo es con [[ -o interactive ]](ksh, bash, zsh) o case $- in (*i*) ...; esac(POSIX)
Stéphane Chazelas
2
Mi bash (versión 4.4.12) en realidad parece desarmarse PS1si no se ejecuta de forma interactiva. Es bastante fácil de probar: PS1=cuckoo bash -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'no imprimirá nada, mientras PS1=cuckoo bash -i -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'imprime el valor $PS1establecido en los archivos de inicio de bash (no imprimirá la cadena "cuco").
FooF
1
@ Stéphane Chazelas: POSIX no requiere que $-contenga iun shell interactivo.
schily
1
Bosh hace esto desde 2012 para ser compatible con ksh. POSIX simplemente no lo requería hasta que el cambio que mantienes sea efectivo.
schily
1
Francamente, diría que llamar [ -n "${PS1}" ] mal es ir demasiado lejos, después de todo, solo se rompe cuando alguien está exportando PS1 (lo que en su respuesta dice que es una mala idea e incluso explica las razones) y eso no afecta bash de todos modos (ya que deshabilita PS1 y PS2 si el shell no es interactivo). Quizás hubiera sido mejor usar una palabra como "desanimado" o hablar sobre las "limitaciones" del enfoque. No creo que esté "mal" del todo. Si algo está mal es exportar PS1, ¡eso es seguro! De todos modos, gracias por entrar en los detalles de esto.
filbranden
1

Parece que este concepto extraño es el resultado del hecho de que bashno comenzó como un clon de shell POSIX sino como un Bourne Shellclon.

Como resultado, el comportamiento interactivo POSIX ( $ENVse llama para shells interactivos) se ha agregado más tarde bashy no se conoce ampliamente.

Hay un shell que garantiza un comportamiento similar. Esto es cshy csh otorga que $prompttiene valores específicos:

$prompt not set          non-interactive shell, test $?prompt.
$prompt set but == ""    .cshrc called by the which(1) command.
$prompt set and != ""    normal interactive shell.

Pero esto no se aplica al Bourne Shell ni a los shells POSIX.

Para un shell POSIX, el único método otorgado es poner código para shells interactivos en el archivo:

$ENV

que tiene un nombre específico de shell. Es por ejemplo

$HOME/.kshrc    for the korn shell
$HOME/.bashrc   for bash
$HOME/.mkshrc   for mksh
$HOME/.shrc     for the POSIX Bourne Shell

Otras personas mencionaron la bandera de shell -i, pero esto no es utilizable para una programación confiable. POSIX no requiere que set -ifuncione, ni que $-contenga un ipara shells interactivos. POSIX solo requiere que sh -iaplique el shell al modo interactivo.

Dado que la variable $PS1se puede importar desde el entorno, puede tener un valor incluso en modo no interactivo. El hecho de que bash unsets PS1en cualquier shell no interactivo no está garantizado por el estándar y no lo hace ningún otro shell.

Entonces, la programación limpia (incluso con bash) consiste en poner los comandos para shells interactivos $HOME/.bashrc.

astuto
fuente
0

Primero voy a hablar de lo que Debian, y la mayoría de las veces también Ubuntu configura para bash. Y este último toque en otros sistemas.

En la configuración de los archivos de inicio de shell hay mucha opinión.
También tengo mi opinión, pero intentaré mostrar ejemplos existentes de configuraciones correctas.
Usaré debuan ya que es bastante fácil encontrar ejemplos de sus archivos.
Y debian se usa mucho, por lo que la configuración se ha probado bien,

¿Cuál es el objetivo de verificar que PS1 esté configurado?

Solo para averiguar si el shell es interactivo.

El valor predeterminado /etc/profileen debian y ubuntu (desde / usr / share / base-files / profile):

if [ "${PS1-}" ]; then
    if [ "${BASH-}" ] && [ "$BASH" != "/bin/sh" ]; then

Se lee el if: si es interactivo (conjunto predeterminado de PS1) y es un shell bash (pero no actúa como predeterminado sh), cambie PS1 a uno nuevo en particular (no el predeterminado).

El valor predeterminado /etc/bash.bashrcen debian también contiene:

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

Lo que está bastante claro en lo que hace: si es interactivo, no fuente (el resto).

Sin embargo, en /etc/skel/.bashrces un ejemplo de la forma correcta de probar un shell interactivo (usando $-):

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

Eso debería mostrar claramente el por qué de PS1 y una alternativa.

El orden correcto

Se debe evitar la configuración que está informando.
La orden (de los ajustes del sistema a la configuración de usuario más específicos (para bash)) es /etc/profile, /etc/bash.bashrc, ~/.profiley finalmente ~/.bashrc. Eso coloca los efectos más amplios (y para más shells) en /etc/profile(que es propiedad de root) seguido de /etc/bash.bashrc(que también es propiedad de root) pero solo afecta a bash. Luego vienen los ajustes personales $HOME, el primero es ~/.profilepara la mayoría de los shells y ~/.bashrc(casi equivalente a ~/.bash_profile), específico solo para bash.

Por tanto, es erróneo fuente ~/.bashrcen ~/.profile, se está transformando un escenario de fiesta de una manera más general que está determinado usuario afectando a más conchas . Excepto si se hace de esta manera :

# ~/.profile: executed by the command interpreter for login shells
# if running bash
if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
    fi
fi

Comprueba que bash se está ejecutando y solo se carga .bashrcsi ese es el caso.

Esta es una decisión previa proveniente de Debian. La razón se explica aquí .

De hecho, a la inversa, el abastecimiento ~/.profileen ~/.bash_profile(o ~/.bashrc) solo está volviendo a aplicar reglas generales que ya deberían haberse cargado a un caso de uso en particular, y por lo tanto "no está tan mal" (no digo "bueno"). Y no digo nada bueno porque puede hacer que el origen de los archivos se repita. Como cuando un subdirectorio carga un padre, ese es un bucle de directorio.

Y es en este abastecimiento cruzado que la verificación de shell interactivo tiene sentido. Solo cuando un shell es interactivo se ~/.bashrccarga, pero a su vez puede estar cargando ~/.profile(o al revés) y es en este caso que podría usarse la comprobación de un shell interactivo.

Isaac
fuente