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

10

Qual é a finalidade do [ -n "$PS1" ] em [ -n "$PS1" ] && source ~/.bash_profile; ? Esta linha está incluída em um .bashrc de um repo de dotfiles.

    
por shotes 15.08.2018 / 06:40

4 respostas

20

Isso está verificando se o shell é interativo ou não. Nesse caso, apenas o arquivo ~/.bash_profile será fornecido se o shell for interativo.

Veja "Esta Shell é interativa?" o manual bash, que cita esse idioma específico. (Também recomenda verificar se o shell é interativo testando se a variável $- special contém o caractere i , que é uma abordagem melhor para esse problema.)

    
por 15.08.2018 / 06:57
18

O que isso faz

Esta é uma maneira generalizada de testar se o shell é interativo. Tenha em atenção que só funciona no bash, não funciona com outras shells. Então está tudo bem (se for bobo) para .bashrc , mas não funcionaria em .profile (que é lido por sh, e bash é apenas uma das possíveis implementações de sh, e não a mais comum). / p>

Por que funciona (apenas no bash!)

Um shell interativo define a variável de shell PS1 para a string de prompt padrão. Portanto, se o shell for interativo, PS1 será definido (a menos que o usuário .bashrc o tenha removido, o que não pode ter acontecido ainda no topo de .bashrc , e você pode considerar que é uma coisa tola de se fazer de qualquer maneira ).

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

Muitos sistemas exportam PS1 para o ambiente. É uma má idéia, porque muitos shells diferentes usam PS1 , mas com uma sintaxe diferente (por exemplo, O prompt do bash escapa é completamente diferente do prompt do zsh escapa ). Mas é bastante difundido que, na prática, ver que PS1 está definido não é um indicador confiável de que o shell é interativo. O shell pode ter herdado PS1 do ambiente.

Por que é usado (mis) aqui

.bashrc é o arquivo que o bash lê na inicialização quando é interativo. Um fato menos conhecido é que o bash também lê .bashrc é um shell de login e as heurísticas de bash concluem que esta é uma sessão remota (o bash verifica se seu pai é rshd ou sshd ). Neste segundo caso, é improvável que PS1 seja definido no ambiente, porque nenhum arquivo de pontos foi executado ainda.

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

  • Se o shell for um shell interativo, isso executará .bash_profile nesse shell. Mas .bash_profile é um script de login. Pode executar alguns programas que devem ser executados apenas uma vez por sessão. Ele 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 de .bash_profile em um shell que não seja de login é prejudicial.
  • Se o shell for um shell de login remoto não interativo, ele não carregará .bash_profile . Mas esse é o caso em que carregar .bash_profile pode ser útil, porque um shell de login não interativo carrega automaticamente /etc/profile e ~/.profile .

Acho que o motivo pelo qual as pessoas fazem isso é para usuários que fazem login por meio de uma GUI (um caso muito comum) e que colocam suas configurações de variáveis de ambiente em .bash_profile em vez de .profile . A maioria dos mecanismos de login da GUI invoca .profile , mas não .bash_profile (a leitura de .bash_profile exigiria 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, eles recebem suas variáveis de ambiente. No entanto, o usuário não obterá suas variáveis de ambiente em aplicativos de GUI, o que é uma fonte muito comum de confusão. A solução aqui é usar .profile em vez de .bash_profile para definir variáveis de ambiente. Adicionar uma ponte entre .bashrc e .bash_profile cria mais problemas do que resolve.

O que fazer em vez disso

Há uma maneira direta e portátil de testar se o shell atual é interativo: teste se a opção -i está ativada.

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

Isso é útil em .bashrc para ler .profile apenas se o shell não for interativo - ou seja, o oposto do que o código faz! Leia .profile se bash for um shell de login (não interativo) e não o leia se for um shell interativo.

if [[ $- != *i* && -r ~/.profile ]]; then . ~/.profile; fi
    
por 15.08.2018 / 08:31
1

Parece que esse conceito estranho é resultado do fato de que bash não começou como um clone de shell POSIX, mas como um clone Bourne Shell .

Como resultado, o comportamento interativo POSIX ( $ENV é chamado para shells interativos) foi adicionado posteriormente a bash e não é amplamente conhecido.

Existe um shell que concede comportamento semelhante. Isso é csh e csh concede que $prompt tenha 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 Bourne Shell nem aos POSIX shells.

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

$ENV

que tem um nome específico de 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 requer que set -i funcione, nem que $- contenha i para shells interativos. O POSIX requer apenas que sh -i imponha o shell no modo interativo.

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

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

    
por 15.08.2018 / 11:41
0

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

Na configuração dos arquivos de inicialização 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 default /etc/profile no debian e no ubuntu (de / usr / share / base-files / profile):

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

O if é lido: se interativo (PS1 padrão definido) e é um bash shell (mas não atuando como um padrão sh ), então altere o PS1 para um novo (não o padrão).

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

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

O que é bem claro no que faz: se interativo não for fonte (o resto).

No entanto, em /etc/skel/.bashrc é um exemplo da maneira correta de testar um shell interativo (usando $- ):

# 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á denunciando deve ser evitada.
O pedido (de configurações do sistema para configurações de usuário mais específicas (para o bash)) é /etc/profile , /etc/bash.bashrc , ~/.profile e finalmente ~/.bashrc . Isso coloca os efeitos mais amplos (e para mais shells) em /etc/profile (que é de propriedade de root) seguido por /etc/bash.bashrc (que também é de propriedade de root), mas afeta apenas o bash. Em seguida, vêm as configurações pessoais em $HOME , a primeira é ~/.profile para a maioria das shells e ~/.bashrc (quase equivalente a ~/.bash_profile ), específico apenas para bash.

Portanto, é errado usar ~/.bashrc em ~/.profile , ele está transformando uma configuração de usuário específica para bash em um mais geral que é afetando mais shells . Exceto se feito desta forma :

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

Verifica se o bash está sendo executado e carrega apenas .bashrc , se for o caso.

Esta é uma decisão do upstream vinda do Debian. A justificativa é explicada aqui .

Na verdade, o inverso, a terceirização de ~/.profile in ~/.bash_profile (ou ~/.bashrc ) está apenas reaplicando regras gerais que já deveriam ter sido carregadas em um caso de uso específico e, portanto, "não tão ruins" ( Eu não estou dizendo "bom"). E eu não estou dizendo bem porque isso pode fazer com que o fornecimento de arquivos faça um loop. Como quando um sub-diretório carrega um pai, isso é um loop de diretório.

E é nesse cruzamento que a verificação de shell interativo faz sentido. Somente quando um shell é interativo é ~/.bashrc carregado, mas, por sua vez, pode estar carregando ~/.profile (ou o contrário) e, nesse caso, a verificação de um shell interativo pode ser usada.

    
por 18.08.2018 / 05:31

Tags