Por que o bashrc verifica se o shell atual é interativo?

54

Na minha instalação do Arch, /etc/bash.bashrc e /etc/skel/.bashrc contêm estas linhas:

# If not running interactively, don't do anything
[[ $- != *i* ]] && return

No Debian, /etc/bash.bashrc tem:

# 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

De acordo com man bash , no entanto, shells não interativos nem lêem esses arquivos:

   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 eu entendi corretamente, os arquivos *.bashrc só serão lidos se BASH_ENV estiver definido para apontar para eles. Isso é algo que não pode acontecer por acaso e só ocorrerá se alguém definir explicitamente a variável de acordo.

Isso parece interromper a possibilidade de que os scripts forneçam automaticamente .bashrc de um usuário definindo BASH_ENV , algo que pode ser útil. Dado que o bash nunca lerá esses arquivos quando executado de forma não-interativa, a menos que seja explicitamente solicitado, por que os arquivos *bashrc padrão não o permitem?

    
por terdon 25.01.2016 / 18:39

3 respostas

59

Esta é uma pergunta que eu ia postar aqui há algumas semanas. Como terdon , eu entendi que um .bashrc é originado apenas para shells Bash interativos, portanto não deve haver necessidade de .bashrc verificar se ele está sendo executado em um shell interativo. Confusamente, all as distribuições que eu uso (Ubuntu, RHEL e Cygwin) tinham algum tipo de verificação (testando $- ou $PS1 ) para garantir que o shell atual é interativo. Eu não gosto de programação de cultos de carga então comecei a entender o propósito de este código no meu .bashrc .

Bash tem um caso especial para shells remotas

Depois de pesquisar o problema, descobri que os shells remotos são tratados de forma diferente. Embora os shells Bash não interativos não executem normalmente os comandos ~/.bashrc na inicialização, um caso especial é feito quando o shell é Chamado pelo daemon de shell remoto :

Bash attempts to determine when it is being run with its standard input connected to a network connection, as when executed by the remote shell daemon, usually rshd, or the secure shell daemon sshd. If Bash determines it is being run in this fashion, it reads and executes commands from ~/.bashrc, if that file exists and is readable. It will not do this if invoked as sh. The --norc option may be used to inhibit this behavior, and the --rcfile option may be used to force another file to be read, but neither rshd nor sshd generally invoke the shell with those options or allow them to be specified.

Exemplo

Insira o seguinte no início de um controle remoto .bashrc . (Se .bashrc for originado por .profile ou .bash_profile , desative temporariamente isso durante o teste):

echo bashrc
fun()
{
    echo functions work
}

Execute os seguintes comandos localmente:

$ ssh remote_host 'echo $- $0'
bashrc
hBc bash
  • Não i in $- indica que o shell é não interativo .
  • Nenhum líder - in $0 indica que o shell não é um shell de login .

As funções do shell definidas no controle remoto .bashrc também podem ser executadas:

$ ssh remote_host fun
bashrc
functions work

Notei que o ~/.bashrc é somente originado quando um comando é especificado como o argumento para ssh . Isso faz sentido: quando ssh é usado para iniciar um shell de login regular, .profile ou .bash_profile são executados (e .bashrc é apenas originado se explicitamente feito por um desses arquivos).

O principal benefício que vejo ao ter o .bashrc originado ao executar um comando remoto (não interativo) é que as funções do shell podem ser executadas. No entanto, a maioria dos comandos em um .bashrc típico só é relevante em um shell interativo, por exemplo, os aliases não são expandidos, a menos que o shell seja interativo.

Transferências de arquivos remotas podem falhar

Normalmente, isso não é um problema quando rsh ou ssh são usados para iniciar um shell de login interativo ou quando shells não interativos são usados para executar comandos. No entanto, pode ser um problema para programas como rcp , scp e sftp que usam shells remotas para transferir dados.

Acontece que o shell padrão do usuário remoto (como o Bash) é iniciado implicitamente ao usar o comando scp . Não há menção a isso na página man - apenas uma menção de que scp usa ssh para sua transferência de dados. Isso tem como consequência que se o .bashrc contiver algum comando que imprima na saída padrão, as transferências de arquivos falharão , por exemplo, scp falha sem erro .

Veja também este relato de bug relacionado à Red Hat de 15 anos atrás, scp quebra quando há um comando echo em / etc / bashrc (que foi encerrado como WONTFIX ).

Por que scp e sftp falham

SCP (cópia segura) e SFTP (Secure File Transfer Protocol) tem seus próprios protocolos para os fins locais e remotos para trocar informações sobre o (s) arquivo (s) sendo transferido (s). Qualquer texto inesperado do terminal remoto é (erroneamente) interpretado como parte do protocolo e a transferência falha. De acordo com uma FAQ do Snail Book

What often happens, though, is that there are statements in either the system or per-user shell startup files on the server (.bashrc, .profile, /etc/csh.cshrc, .login, etc.) which output text messages on login, intended to be read by humans (like fortune, echo "Hi there!", etc.).

Such code should only produce output on interactive logins, when there is a tty attached to standard input. If it does not make this test, it will insert these text messages where they don't belong: in this case, polluting the protocol stream between scp2/sftp and sftp-server.

The reason the shell startup files are relevant at all, is that sshd employs the user's shell when starting any programs on the user's behalf (using e.g. /bin/sh -c "command"). This is a Unix tradition, and has advantages:

  • The user's usual setup (command aliases, environment variables, umask, etc.) are in effect when remote commands are run.
  • The common practice of setting an account's shell to /bin/false to disable it will prevent the owner from running any commands, should authentication still accidentally succeed for some reason.

Detalhes do protocolo SCP

Para os interessados nos detalhes de como o SCP funciona, encontrei informações interessantes em Como funciona o protocolo SCP que inclui detalhes sobre Execução de scp com perfis de shell falantes no lado remoto? :

For example, this can happen if you add this to your shell profile on the remote system:

echo ""

Why it just hangs? That comes from the way how scp in source mode waits for the confirmation of the first protocol message. If it's not binary 0, it expects that it's a notification of a remote problem and waits for more characters to form an error message until the new line arrives. Since you didn't print another new line after the first one, your local scp just stays in a loop, blocked on read(2). In the meantime, after the shell profile was processed on the remote side, scp in sink mode was started, which also blocks on read(2), waiting for a binary zero denoting the start of the data transfer.

Conclusão / TLDR

A maioria das instruções em um típico .bashrc é útil apenas para um shell interativo - não ao executar comandos remotos com rsh ou ssh . Na maioria das situações, não é desejado definir variáveis de shell, aliases e funções definidoras - e imprimir qualquer texto como padrão é prejudicial ao transferir arquivos usando programas como scp ou sftp . Sair depois de verificar se o shell atual é não interativo é o comportamento mais seguro para .bashrc .

    
por 25.01.2016 / 21:50
15

A página man não menciona que bash também origina .bashrc para shells remotas não interativas, como em

ssh hostname command

link

 COMMAND        EXECUTE BASHRC
 --------------------------------
 bash -c foo        NO
 bash foo           NO
 foo                NO
 rsh machine ls     YES (for rsh, which calls 'bash -c')
 rsh machine foo    YES (for shell started by rsh) NO (for foo!)
 echo ls | bash     NO
 login              NO
 bash               YES

link

          /* If we were run by sshd or we think we were run by rshd, execute
             ~/.bashrc if we are a top-level shell. */
          if ((run_by_ssh || isnetconn (fileno (stdin))) && shell_level < 2)
            {
              maybe_execute_file (SYS_BASHRC, 1);
              maybe_execute_file (bashrc_file, 1);
    
por 25.01.2016 / 18:44
5

Por convenção, .bashrc é o local em que o usuário armazena a configuração personalizada para o shell.

Essa configuração customizada pode ser variáveis de ambiente, aliases, prompt de fantasia. Com um shell não interativo, essas poucas coisas não têm sentido. Além disso, um shell não interativo pode ser chamado em muitos contextos, você não tem certeza se essas variáveis de ambiente podem levar a casos de falso negativo ou até vulneráveis à segurança.

Um exemplo mais próximo é um alias como:

alias cp='cp -i'

Depois, pendure seu shell não interativo para sempre.

Assim, a verificação é executada na parte superior de .bashrc para garantir que não tenhamos problemas.

Como o shell pode ser chamado como shell login não interativo, bloquear explicitamente o sourcing *bashrc não faz sentido.

Quando o shell era chamado como shell de login não interativo , ele origina /etc/profile e, em seguida, obtém o primeiro encontrado em ~/.bash_profile , ~/.bash_login e ~/.profile :

When bash is invoked as an interactive login shell, or as a non-interactive shell with the --login option, it first reads and executes commands from the file /etc/profile, if that file exists. After reading that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in that order, and reads and executes commands from the first one that exists and is readable.

Nada impede que esses arquivos obtenham .bashrc , portanto, fazer a verificação dentro de .bashrc é mais seguro e simplificar as coisas.

    
por 25.01.2016 / 19:28

Tags