Chamar o comando subshell com rcfile carregado

3

Eu tenho um código Python que faz: (esta é uma versão simplificada)

shell = os.environ['SHELL']
os.environ['PATH'] = ... # some new PATH value
if call(shell, '-c', 'program_that_checks_that_PATH_is_sane'): # hasn't been mangled by the rcfile
    sys.exit('fix your .bashrc|.zshrc|.config/fish')
call(shell) # drops the user in a shell

Eu estupidamente acreditei que $SHELL -c teria sido suficiente para meus propósitos, mas recentemente descobri que a verificação PATH não estava funcionando na bash

Tentei analisar algumas das soluções sugeridas , mas bash é muito mais frágil do que eu esperava

Basicamente, descobri que BASH_ENV , ou a origem manual com bash -c "source ~/.bashrc; do_your_stuff" não funcionará se alguns erros puderem ser acionados pelo arquivo originado

Esses mesmos erros não são um problema se invocar bash interativamente (isto é, bash -i ignorará erros e bash -c falhará silenciosamente). Mas bash -i não é uma solução viável ( Eu acho que isso é porque bash -i "roubará" o stdin, parando assim o processo python de invocação)

A solução também não pode ser simplesmente "não use um arquivo ~ / .bashrc":

  • O código precisa ser robusto diante de ambientes mal configurados
  • Isso acontece com um padrão .bashrc , como o um fornecido pelo ubuntu (esta é uma versão antiga)

O erro realmente acontece (encontrado por set -xe antes do fornecimento) dentro de /usr/share/bash-completion/bash_completion nesta linha: [[ -f /etc/slackware-version ]] && sysvdirs=( /etc/rc.d ) (como isso pode falhar no processo de terceirização está além de mim)

Depois de vários ajustes, descobri como eu poderia detectar esses erros ao pesquisar:

set -e && . ~/.bashrc & wait %% ; echo $?

Estranhamente, o seguinte falhará:

set -e ; . ~/.bashrc & wait %% ; echo $?

Ainda assim, isso não é bom o suficiente, porque nem

bash -c "set -e && . ~/.bashrc & wait %% ; echo \$?"
call(['bash', '-c', 'set -e && . ~/.bashrc & wait %% ; echo $?'])

imprime um código de saída diferente de zero. Eu também prefiro evitar confiar em código específico do shell como este.

Como posso ter certeza de que ao invocar bash -c , o .bashrc será corretamente carregado?

Como alternativa a todo esse problema, estou pensando em inserir o. {bash, zsh} rc, para identificar problemas com o PATH. É uma solução ruim, mas cobriria 90% dos casos e evitaria processos adicionais.

import re
r = '^export PATH\s?=\s?([^:]+:)+(\$PATH|\${PATH})'
g = re.match(r, 'export PATH=/usr/bin:$PATH').groups()
any(check_collision(x[:-1]) for x in g[:-1])

editar:

O sinalizador bash -i parece ser a metade-solução mais simples e promissora, mas seu comportamento não é óbvio, alguns exemplos para ajudar as pessoas a entender:

Isso executa ls e, em seguida, nos deixa no prompt do Python, com o Python em execução

python3 -ic "import os; os.system(\"bash -c ls\")"

Isso interrompe o processo do Python

python3 -ic "import os; os.system(\"bash -ic ls\")"

Fornecer um stdin diferente ao processo filho não é suficiente:

python3 -ic "import subprocess;f=open('/dev/null');subprocess.call(['bash', '-ic', 'ls'], stdin=f)"

Finalmente, bash -ic na verdade não recebe comandos adicionais do stdin, os dois comandos a seguir têm exatamente o mesmo comportamento:

 echo echo sed | bash -c "sed 's/[^ ]*$/bash/'"
 echo echo sed | bash -ic "sed 's/[^ ]*$/bash/'"
    
por berdario 02.03.2015 / 16:46

1 resposta

-1

Não use a variável de ambiente SHELL em um script. Essa é a shell interativa preferida do usuário. Você não sabe nada sobre a sintaxe que suporta. Pode ser tcsh, zsh, fish, rc,…

Não carregue .bashrc de um script. É provável que o usuário coloque as coisas que dependem de bash sendo interativo e tendo um terminal; ele pode travar ou reconfigurar seu terminal ou ocultar a saída do script ou qualquer outra coisa ruim. Quando você executa bash -c … , .bashrc não é carregado, e isso é planejado.

Se você precisa verificar se PATH é são, você pode fazê-lo em Python, você não precisa invocar um shell para isso. Se por “o código tem que ser robusto em face de ambientes mal configurados” você quer dizer que seu script Python deve tentar replicar o PATH de shells interativos, mesmo que não seja iniciado a partir de um, então não: você está longe mais propensos a bagunçar de formas estranhas (e, em particular, a anular uma configuração explícita do usuário) do que você provavelmente ajudará o usuário.

Se o seu script estiver colocando o usuário em um shell interativo por algum motivo, execute os.environ['SHELL'] , retornando para pwd.getpwuid(os.geteuid()).pw_shell se a variável de ambiente não estiver definida. Isto irá executar um shell interativo, desde que o seu script esteja rodando em um terminal, você não precisa tentar adivinhar -i ou outras opções que podem ou não significar o que você espera. Se o seu script não estiver sendo executado em um terminal, ele não terá nenhum negócio tentando interagir com o usuário e não deverá iniciar um shell interativo.

    
por 03.03.2015 / 01:37

Tags