Python CGI脚本中os.system()无法获取系统命令输出问题
问题原因
CGI本身没有禁止执行系统命令,你调用os.system('twopi -V')返回状态码0,已经证明命令实际执行成功,看不到输出是os.system的特性加CGI运行环境的机制共同导致的:
os.system()不会捕获命令的输出内容,只会返回命令的退出状态码。命令运行时产生的所有输出,会直接写入当前进程绑定的标准输出(stdout)文件描述符。- 你在SSH命令行手动运行Python脚本时,进程的stdout直接绑定到终端,所以命令输出会直接打印到屏幕上,之后
print(os.system(...))打出返回值0,就是你看到的正常效果。 - 但CGI模式下,Web服务器只会收集Python进程主动通过标准输出打印的、符合CGI协议格式的响应内容,子进程直接写入stdout文件描述符的内容不会被Web服务器正确转发给请求端,最终你只能拿到
print打出来的状态码0,看不到命令实际输出。
另外绝大多数虚拟主机的CGI运行环境不会加载你SSH登录时的shell配置,PATH环境变量非常精简,哪怕解决了输出捕获问题,不写命令绝对路径也很容易触发"命令不存在"的错误。
解决方案
放弃os.system,使用Python标准库的subprocess模块调用外部命令,主动捕获命令的输出内容,再通过print返回给HTTP响应即可。
以下是可直接运行的CGI脚本示例:
#!/home/me/virtualenv/public_html/chartex/3.6/bin/python3.6 import os import subprocess # CGI协议强制要求先输出响应头,头和正文之间必须空一行 print("Content-Type: text/plain; charset=utf-8") print() # 先在SSH里执行 which twopi、which ls 拿到你服务器上的实际绝对路径,替换下面的路径 # 调用twopi -V twopi_res = subprocess.run( ["/usr/local/bin/twopi", "-V"], capture_output=True, text=True ) print("=== twopi版本信息 ===") print("退出码:", twopi_res.returncode) print("输出内容:") print(twopi_res.stderr) # 注意graphviz的版本信息默认输出到stderr不是stdout # 调用ls ls_res = subprocess.run( ["/bin/ls", os.path.abspath('.')], capture_output=True, text=True ) print("\n=== 当前目录文件列表 ===") print(ls_res.stdout)
注意事项
- 所有外部命令必须写绝对路径:CGI运行时的
PATH默认不包含你自定义安装软件的目录,写绝对路径可以彻底避免命令找不到的问题。 - 调用
subprocess.run时传参数列表即可,不要加shell=True拼接shell命令字符串,避免产生命令注入安全漏洞。 - 部分命令会把提示、版本信息输出到标准错误(stderr)而不是标准输出(stdout),上面的示例用
capture_output=True会同时捕获两个流的内容,不会漏信息。
内容的提问来源于stack exchange,提问作者jjon
相关产品推荐
相关产品推荐

