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/'"