Objetivo de [-n "$ PS1"] no bashrc

10

A que propósito serve o [ -n "$PS1" ]in [ -n "$PS1" ] && source ~/.bash_profile;? Esta linha está incluído em um .bashrcde um dotfiles repo .

shotes
fonte

Respostas:

20

Isso está verificando se o shell é interativo ou não. Nesse caso, apenas procure o ~/.bash_profilearquivo se o shell for interativo.

Consulte "Este shell é interativo?" no manual do bash, que cita esse idioma específico. (Também recomenda verificar se o shell é interativo, testando se a $-variável especial contém o icaractere, o que é uma abordagem melhor para esse problema.)

filbranden
fonte
pelo menos o bash desabilitará o PS1 e o PS2 se o shell for interativo . Você pode ver por si mesmo, com ( export PS1='abc$ '; bash -c 'echo "[$PS1]"' ), o que simplesmente imprime []. Parece que o zsh não faz o mesmo, pelo menos a partir de um experimento ... De qualquer forma, a intenção do [ -n "$PS1" ]é verificar se o shell é interativo ou não.
filbranden
3
Isso bashdesativa o PS1 quando não interativo (erro de digitação no seu comentário anterior) é um bug da IMO, o PS1 não é uma variável específica do bash, não há negócios em desabilitá-lo. É o único shell que faz isso (embora yashtambém seja definido PS1como um valor padrão mesmo quando não interativo).
Stéphane Chazelas
1
Como o código em questão está em um arquivo específico do bash, isso parece uma resposta razoável. Outras respostas abordam o caso mais geral de especificações POSIX ou outras conchas. Você respondeu "qual é o objetivo disso?" intenção na questão, deixando de lado o restante. É bom saber o que o bash está fazendo e, possivelmente, melhores maneiras de atingir o objetivo também.
Jeff Schaller
Eu consideraria esta resposta mais completa se sugerisse uma alternativa mais confiável (digamos [[ $- = *i* ]] && source ~/.bash_profile).
Charles Duffy
@CharlesDuffy Francamente, não acho que exista muita coisa errada [ -n "${PS1}" ], mas ainda atualizei minha resposta para destacar que o manual do bash também sugere / recomenda a inspeção $-para determinar se o shell é interativo, espero que você ache que melhora a resposta. Felicidades!
filbranden
19

O que isso faz

Essa é uma maneira generalizada de testar se o shell é interativo. Cuidado que ele só funciona no bash, não funciona com outras conchas. Portanto, está tudo bem (se for bobo) .bashrc, mas não funcionaria .profile(o que é lido por sh, e o bash é apenas uma das implementações possíveis do sh, e não a mais comum).

Por que funciona (apenas no bash!)

Um shell interativo define a variávelPS1 do shell como a sequência de prompt padrão. Portanto, se o shell é interativo, PS1está definido (a menos que o usuário o .bashrctenha removido, o que ainda não pode ter acontecido no topo .bashrc, e você pode considerar que é uma coisa boba de se fazer).

O inverso é verdadeiro no bash: instâncias não interativas do bash desativadas PS1quando iniciadas. Observe que esse comportamento é específico do bash e é sem dúvida um bug (por que bash -c '… do stuff with $var…'não funcionaria quando varé PS1?). Mas todas as versões do bash até o 4.4 (inclusive a versão mais recente que eu escrevo) fazem isso.

Muitos sistemas exportam PS1para o meio ambiente. É uma má idéia, porque muitos shells diferentes usam, PS1mas com uma sintaxe diferente (por exemplo , escapes de prompt do bash são completamente diferentes dos escapes de prompt do zsh ). Mas é bastante difundido que, na prática, ver o que PS1está definido não é um indicador confiável de que o shell seja interativo. O shell pode ter herdado PS1do ambiente.

Por que é (mis) usado aqui

.bashrcé o arquivo que o bash lê na inicialização quando é interativo. Um fato menos conhecido é que o bash também lê .bashrcum shell de logon e as heurísticas do bash concluem que esta é uma sessão remota (o bash verifica se seu pai é rshdou sshd). Nesse segundo caso, é improvável que PS1isso seja definido no ambiente, porque nenhum arquivo de ponto foi executado ainda.

No entanto, a maneira como o código usa essas informações é contraproducente.

  • Se o shell for um shell interativo, ele será executado .bash_profilenesse shell. Mas .bash_profileé um script de tempo de login. Pode executar alguns programas que devem ser executados apenas uma vez por sessão. Pode substituir algumas variáveis ​​de ambiente que o usuário definiu deliberadamente para um valor diferente antes de executar esse shell. A execução .bash_profileem um shell sem logon é perturbadora.
  • Se o shell for um shell de logon remoto não interativo, não será carregado .bash_profile. Mas este é o caso em que o carregamento .bash_profilepode ser útil, porque um shell de login não interativo não carrega /etc/profilee automaticamente ~/.profile.

Eu acho que a razão pela qual as pessoas fazem isso é para usuários que efetuam login através de uma GUI (um caso muito comum) e que colocam suas configurações de variáveis ​​de ambiente em .bash_profilevez de .profile. A maioria dos mecanismos de login da GUI é chamada, .profilemas não .bash_profile(a leitura .bash_profileexigiria a execução do bash como parte da inicialização da sessão, em vez de sh). Com essa configuração, quando o usuário abre um terminal, ele obtém suas variáveis ​​de ambiente. No entanto, o usuário não obterá suas variáveis ​​de ambiente nos aplicativos GUI, o que é uma fonte muito comum de confusão. A solução aqui é usar em .profilevez de .bash_profiledefinir variáveis ​​de ambiente. Adicionar uma ponte entre .bashrce .bash_profilecria mais problemas do que resolve.

O que fazer em vez disso

Existe uma maneira simples e portátil de testar se o shell atual é interativo: teste se a opção -iestá ativada.

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

Isso é útil .bashrcpara ler .profileapenas se o shell não for interativo - ou seja, o oposto do que o código faz! Leia .profilese o bash é um shell de logon (não interativo) e não o leia se for um shell interativo.

if [[ $- != *i* && -r ~/.profile ]]; then . ~/.profile; fi
Gilles 'SO- parar de ser mau'
fonte
4
Pode ser interessante notar que a melhor maneira de testar se um shell é interativo é com [[ -o interactive ]](ksh, bash, zsh) ou case $- in (*i*) ...; esac(POSIX)
Stéphane Chazelas
2
Meu bash (versão 4.4.12) realmente parece desconfigurado PS1se não for executado de forma interativa. É fácil testar: PS1=cuckoo bash -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'não imprime nada, enquanto PS1=cuckoo bash -i -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'imprime o valor $PS1definido nos arquivos de inicialização do bash (não imprime a string "cuckoo").
FooF 15/08/19
1
@ Stéphane Chazelas: O POSIX não exige que $-contenha ium shell interativo.
schily
1
O Bosh faz isso desde 2012 para ser compatível com o ksh. Apenas não foi exigido pelo POSIX até que a alteração na sua mudança se torne efetiva.
Schily
1
Francamente, eu diria que ligar [ -n "${PS1}" ] errado está indo um pouco longe demais, afinal só quebra quando alguém está exportando o PS1 (que na sua resposta você diz que é uma péssima idéia e até explica as razões) e isso não afeta bash de qualquer maneira (já que desabilita o PS1 e o PS2 se o shell não for interativo.) Talvez usar uma palavra como "desanimado" ou falar sobre as "limitações" da abordagem fosse melhor. Eu não acho que esteja "errado" por completo. Se algo estiver errado na exportação do PS1, com certeza! De qualquer forma, obrigado por entrar em detalhes.
filbranden
1

Parece que esse conceito estranho é resultado do fato de bashnão ter começado como um clone do shell POSIX, mas como um Bourne Shellclone.

Como resultado, o comportamento interativo do POSIX ( $ENVchamado de shells interativos) foi adicionado posteriormente bashe não é amplamente conhecido.

Há um shell que concede comportamento semelhante. São cshconcessões e csh que $promptpossuem 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.

Mas isso não se aplica ao shell Bourne nem aos shells POSIX.

Para um shell POSIX, o único método concedido é colocar código para shells interativos no arquivo:

$ENV

que tem um nome específico do shell. É por exemplo

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

Outras pessoas mencionaram o sinalizador de shell -i, mas isso não é utilizável para programação confiável. O POSIX não exige que set -ifuncione nem que $-contenha um ipara shells interativos. O POSIX exige apenas que imponha sh -io shell ao modo interativo.

Como a variável $PS1pode ser importada do ambiente, ela pode ter um valor mesmo no modo não interativo. O fato de que bash unsets PS1em qualquer shell não interativo não é concedido pelo padrão e não é feito por qualquer outro shell.

Portanto, a programação limpa (mesmo com bash) é colocar os comandos para shells interativos $HOME/.bashrc.

esperto
fonte
0

Vou falar primeiro sobre o que o Debian, e na maioria das vezes também o Ubuntu define para o bash. E último toque em outros sistemas.

Na configuração dos arquivos de início do shell, há muita opinião.
Eu também tenho minha opinião, mas tentarei mostrar exemplos existentes de configurações corretas.
Vou usar o debuan, pois é muito fácil encontrar exemplos de seus arquivos.
E o debian é muito usado, então as configurações foram bem testadas,

Qual é o objetivo de verificar se o PS1 está definido?

Apenas para descobrir se o shell é interativo.

O padrão /etc/profileno debian e no ubuntu (de / usr / share / arquivos-base / perfil):

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

O if é lido: se interativo (conjunto padrão do PS1) e é um shell bash (mas não atua como padrão sh), altere o PS1 para um novo em particular (não o padrão).

O padrão /etc/bash.bashrcno debian também contém:

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

O que é bastante claro no que faz: se interativo, não faça a fonte (o resto).

No entanto, em /etc/skel/.bashrcé um exemplo da forma correcta de teste para uma concha interactivo (utilizando $-):

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

Isso deve mostrar claramente o porquê do PS1 e uma alternativa.

A ordem correta

A configuração que você está relatando deve ser evitada.
A ordem (de configurações do sistema para mais definições específicas do usuário (para bash)) é /etc/profile, /etc/bash.bashrc, ~/.profilee, finalmente ~/.bashrc. Isso coloca os efeitos mais amplos (e para mais shells) em /etc/profile(que pertence à raiz), seguido por /etc/bash.bashrc(que também pertence à raiz), mas afeta apenas o bash. Em seguida, entram as configurações pessoais $HOME, a primeira é ~/.profilepara a maioria das conchas e ~/.bashrc(quase equivalente a ~/.bash_profile), específica apenas para o bash.

Por isso, é errada a fonte ~/.bashrcem ~/.profile, ele está transformando uma configuração para a festança de uma forma mais geral que é usuário específico afetando mais conchas . Exceto se feito desta maneira :

# ~/.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

Ele verifica se o bash está em execução e só carrega .bashrcse for esse o caso.

Esta é uma decisão upstream vinda do Debian. A lógica é explicada aqui .

Na verdade, o inverso, o abastecimento ~/.profileem ~/.bash_profile(ou ~/.bashrc) só é re-aplicação de regras gerais que devem ter sido já carregados para um caso de uso particular, e, portanto, "não é tão ruim" (não estou dizendo "bom"). E não estou dizendo bom, porque isso pode causar um loop no fornecimento de arquivos. Como quando um subdiretório carrega um pai, isso é um loop de diretório.

E é nessa fonte cruzada que a verificação do shell interativo faz sentido. Somente quando um shell é interativo é ~/.bashrccarregado, mas por sua vez, ele pode estar carregando ~/.profile(ou vice-versa) e é nesse caso que a verificação de um shell interativo pode ser usada.

Isaac
fonte