Por que um shell de login bash que não interage ignora o / etc / profile quando invocado pelo su -?

3

Estou usando su para iniciar tmux como um usuário específico, assim:

$ su - someuser -c tmux

No entanto, isso resulta em uma mensagem de erro:

tmux: need UTF-8 locale (LC_CTYPE) but have ANSI_X3.4-1968

O erro ocorre porque a variável de ambiente LANG não está definida. Um novo login shell normalmente define isso via /etc/profile , mas a invocação através de su não usa /etc/profile , embora o uso de su - solicite e pareça produzir um shell de login. / p>

O usuário someuser para o qual isso não funciona tem o shell bash . No entanto, ele funciona com outros shells, como dash ou zsh . Um shell de login bash interativo também funciona como esperado.

Por que bash ignora /etc/profile quando é invocado não interativamente por meio de su - ?

Minha pesquisa (abaixo) indicaria que o problema é que bash , em contraste com outros shells, se comporta de maneira diferente quando um shell de login é solicitado com um hífen principal em $0 (o que su faz) versus -l (ou --login ) opção de linha de comando.

Pesquisa

(isso é um pouco detalhado, mas eu queria registrar minhas investigações)

man su explica que o argumento -c é usado para

Pass command to the shell with the -c option.

e o argumento -

Start the shell as a login shell with an environment similar to a real login

Portanto, su - someuser -c cmd deve executar cmd em um shell de login para someuser com um ambiente semelhante a um login real.

Um comando de teste é útil para demonstrar:

$ testcmd='shopt -q login_shell || echo -n non-; echo login shell $0 $LANG'

Tentando isso com su para shells de login e não-login:

$ su - someuser -c "$testcmd"
login shell -bash
$ su someuser -c "$testcmd"
non-login shell bash en_GB.UTF-8

Quando um shell de login é iniciado, o ambiente não está configurado ( LANG não está definido porque /etc/profile não é usado). O ambiente é herdado de quando um shell não-login é lançado, então o valor de LANG é aquele do shell de inicialização, já que a limpeza é demonstrada de antemão:

$ LANG= su someuser -c "$testcmd"
non-login shell bash

Nos exemplos acima, someuser tem o shell bash . Os mesmos resultados são obtidos com sh (que é apenas um link simbólico para bash ). Se zsh for usado, as coisas funcionam conforme o esperado:

$ ztestcmd='[[ -o login ]] || echo -n non-; echo login shell $LANG'
$ LANG= su - someuser -s /bin/zsh -c "$ztestcmd"
Password: 
login shell -zsh en_GB.UTF-8
$ LANG= su someuser -s /bin/zsh -c "$ztestcmd"
Password: 
non-login shell zsh
su someuser -s /bin/zsh -c "$ztestcmd"
Password: 
non-login shell zsh en_GB.UTF-8

Também funciona com dash . Portanto, o problema parece estar com bash . Investigando bash ...

Funciona como esperado se um shell interativo for lançado:

$ su - someuser
$ LANG= shopt -q login_shell || echo -n non-; echo login shell $0 $LANG
login shell -bash en_GB.UTF-8

Portanto, bash interativa e não interativa lançada por meio de su se comporta de maneira diferente. Que tal lançar bash diretamente?

$ LANG= bash -c "$testcmd"
non-login shell bash
$ LANG= bash -l -c "$testcmd"
login shell bash en_GB.UTF-8

Isso funciona como esperado.

O Manual GNU Bash afirma que

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

De acordo com o homem su , sua opção - não usa --login , mas sim

sets argv[0] of the shell to - in order to make the shell a login shell

O que pode ser comprovado:

$ su - someuser -c 'echo $0'
Password: 
-bash

Podemos testar a nós mesmos que um hífen principal em $0 fornece um shell de login:

$ (LANG= exec -a '-' bash -c "$testcmd")
login shell -

Assim, um hífen principal em $0 produz um shell de login, mas bash não executa /etc/profile quando invocado dessa maneira, o que é inconsistente com o comportamento de sua opção -l e também com outros shells, como zsh ou dash . Também é inconsistente com seu próprio comportamento quando lançado interativamente:

$ (LANG= exec -a '-' bash)
$ shopt -q login_shell || echo -n non-; echo login shell $0 $LANG
login shell - en_GB.UTF-8

Infelizmente, isso não está de acordo com a documentação citada acima, porque isso não define o comportamento de um shell de login não interativo que não foi lançado com -l .

    
por starfry 10.01.2017 / 12:50

1 resposta

2

Ele faz o que diz na lata.

Lembre-se da documentação :

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

Explica corretamente que ele lê e executa /etc/profile como um shell de login interativo ou como um shell não interativo com a opção --login . Ele não diz que isso é feito como um shell de login não interativo indicado por $0 , começando com um hífen.

No entanto, esse comportamento é por design e é algo que pode ser alterado em tempo de compilação, removendo o comentário de uma opção de tempo de compilação encontrada em config-top.h :

/* Define this to make non-interactive shells begun with argv[0][0] == '-'
run the startup files when not in posix mode. */
/* #define NON_INTERACTIVE_LOGIN_SHELLS */

Isso, no entanto, levanta a questão: por que esse comportamento foi implementado quando não é, IMHO , como seria razoável esperar que funcionasse, especialmente quando não é implementado assim em outras conchas.

Dito isso, não acho que o shell padrão POSIX defina shell de login para que Eu acho que está aberto à interpretação.

Aqui está uma solução para o problema original que funciona com bash :

$ su - someuser -c 'bash -l -c tmux'
    
por 10.01.2017 / 12:50

Tags