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 daemonsshd
. 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 assh
. 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 neitherrshd
norsshd
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 (likefortune
,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 betweenscp2
/sftp
andsftp-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 localscp
just stays in a loop, blocked onread(2)
. In the meantime, after the shell profile was processed on the remote side,scp
in sink mode was started, which also blocks onread(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
.