lundi 2 mars 2015

Invoke subshell command with loaded rcfile


I have some Python code that does: (this is a simplified version)



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


I foolishly believed that $SHELL -c would've been enough for my purposes, but I recently discovered that the PATH check wasn't working on bash


I tried to look at some of the suggested solutions, but bash is much more brittle than i expected


Basically, I discovered that BASH_ENV, or manually sourcing with bash -c "source ~/.bashrc; do_your_stuff" won't work if some errors can be triggered by the sourced file


These same errors are not a problem if invoking bash interactively (that is, bash -i will ignore errors, and bash -c will silently fail). But bash -i is not a workable solution (I guess that this is because bash -i will "steal" the stdin, thus stopping the invoking python process)


The solution can't also simply be "don't use a broken ~/.bashrc":



  • The code has to be robust in face of poorly configured environments

  • This happens with a default .bashrc, like the one supplied by ubuntu (this is an old version)


The error actually happens (found it by set -xe before sourcing) inside /usr/share/bash-completion/bash_completion at this line: [[ -f /etc/slackware-version ]] && sysvdirs=( /etc/rc.d ) (how can this fail the process of sourcing is beyond me)


After lots of tweaking, I found out how could I detect such errors when sourcing:



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


Weirdly, the following will fail instead:



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


Still, this isn't good enough, because neither



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


will print a nonzero exit code. I'd also prefer to avoid to rely on shell specific code like this.


How can I make sure that when invoking bash -c the .bashrc will be correctly loaded?


As an alternative to this whole issue, I'm thinking of munging the .{bash,zsh}rc, to identify problems with the PATH. It's a poor solution, but it'd cover 90% of the cases, and avoid forking extra processes.



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])


Aucun commentaire:

Enregistrer un commentaire