我有一些Python代码可以做到:(这是一个简化版本)
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
我愚蠢地认为这$SHELL -c
足以满足我的目的,但我最近发现 PATH 检查在 bash 上不起作用
我试着看一些建议的解决方案,但是 bash 比我预想的要脆弱得多
基本上,我发现,如果源文件可以触发某些错误,BASH_ENV
则手动源将不起作用bash -c "source ~/.bashrc; do_your_stuff"
如果以交互方式调用 bash,这些相同的错误不是问题(即,bash -i
将忽略错误,并且bash -c
将默默地失败)。但是bash -i
不是一个可行的解决方案(我猜这是因为bash -i
会“窃取”标准输入,从而停止调用python进程)
解决方案也不能简单地是“不要使用损坏的 ~/.bashrc”:
- 面对配置不佳的环境,代码必须具有鲁棒性
- 这种情况发生在默认情况下
.bashrc
,例如ubuntu 提供的一个(这是旧版本)
错误实际上发生在这一行set -xe
内部(在采购之前发现它) :(这怎么会失败,采购过程超出了我的范围)/usr/share/bash-completion/bash_completion
[[ -f /etc/slackware-version ]] && sysvdirs=( /etc/rc.d )
经过大量调整后,我发现如何在采购时检测到此类错误:
set -e && . ~/.bashrc & wait %% ; echo $?
奇怪的是,以下操作会失败:
set -e ; . ~/.bashrc & wait %% ; echo $?
尽管如此,这还不够好,因为两者都不是
bash -c "set -e && . ~/.bashrc & wait %% ; echo \$?"
call(['bash', '-c', 'set -e && . ~/.bashrc & wait %% ; echo $?'])
将打印非零退出代码。我还希望避免依赖这样的 shell 特定代码。
如何确保在调用时正确加载bash -c
?.bashrc
作为整个问题的替代方案,我正在考虑修改 .{bash,zsh}rc 来识别 PATH 的问题。这是一个糟糕的解决方案,但它可以覆盖 90% 的情况,并且避免分叉额外的进程。
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])
编辑:
bash-i
标志似乎是最简单、最有前途的半解决方案,但它的行为并不明显,一些例子可以帮助人们理解:
这会运行 ls,然后让我们进入 Python 提示符,Python 正在运行
python3 -ic "import os; os.system(\"bash -c ls\")"
这会停止 Python 进程
python3 -ic "import os; os.system(\"bash -ic ls\")"
为子进程提供不同的标准输入是不够的:
python3 -ic "import subprocess;f=open('/dev/null');subprocess.call(['bash', '-ic', 'ls'], stdin=f)"
最后,bash -ic
实际上并没有从标准输入获取额外的命令,以下两个命令具有完全相同的行为:
echo echo sed | bash -c "sed 's/[^ ]*$/bash/'"
echo echo sed | bash -ic "sed 's/[^ ]*$/bash/'"
答案1
不要SHELL
在脚本中使用环境变量。这是用户首选的交互式 shell。您对它支持的语法一无所知。可能是 tcsh、zsh、fish、rc……
不要.bashrc
从脚本加载。用户可能会放置依赖 bash 进行交互并具有终端的东西;它可能会挂起,或重新配置您的终端,或隐藏脚本的输出,或许多不好的事情。当您运行时bash -c …
,.bashrc
不会加载,这是设计使然的。
如果您需要检查是否PATH
正常,可以在 Python 中执行,无需为此调用 shell。如果“代码必须在面对配置不良的环境时保持健壮”,您的意思是您的 Python 脚本应该尝试复制交互式 shell 的 PATH,即使它不是从一个 shell 启动的,那么就不要这样做:您还差得很远。更有可能以奇怪的方式搞砸(特别是覆盖用户的显式设置),而不是帮助用户。
如果您的脚本由于某种原因将用户放入交互式 shell,则执行 run ,如果未设置环境变量则os.environ['SHELL']
返回到。pwd.getpwuid(os.geteuid()).pw_shell
如果您的脚本在终端中运行,这将运行一个交互式 shell,您不需要尝试猜测-i
或其他选项,这些选项可能意味着也可能不意味着您希望的内容。如果您的脚本没有在终端中运行,那么它就不会尝试与用户交互,也不应该启动交互式 shell。