0

我正在尝试在下面的 CGI 脚本上从 Python 运行 shell 命令。有人可以向我解释为什么exit_status0我在 shell 上执行脚本时,而exit_status127我从网络浏览器运行它时?提前致谢。另外,如果我尝试像echo something(例如)这样的子进程,在终端中运行时退出状态将为零,但在浏览器中完成时exit_status = subprocess.call('echo something', shell=True)会有一个。500 Internal Server Error我真的很纳闷...

#!/usr/bin/env python

import subprocess

print 'Content-type: text/html\n\n'  
print '<html><head>'
print '</head><body>'

exit_status = subprocess.call('ls', shell=True)

print exit_status

print '</body></html>'
4

1 回答 1

2

正如@abarnert 在评论中提到的那样,shell=True除非您别无选择(例如,您绝对需要系统外壳的扩展功能),否则通常应避免使用。作为子流程文档的这一部分解释说,shell 的使用引入了各种严重的安全问题,其中任何命令(包括参数)都是从外部来源(例如表单提交)获得的。除了这些问题之外,您还相信在主机系统上正确配置了 shell,这对于特殊用户(例如用于 Web 服务的用户)来说并不总是一个安全的假设。除了安全漏洞之外,为了执行可以直接执行的命令而派生一个额外的 shell 进程也是一种浪费。此外,您可能会因依赖系统外壳而损害您的可移植性,因为不同的平台将对命令字符串应用不同的扩展和规则。

使用subprocess时最好将命令作为参数列表而不是单个字符串传递,这在使用shell=False. 可能并非所有平台都需要它,但它肯定在 Linux 上,因此可以使您的代码更具可移植性。此外,自己拆分参数可以避免忘记转义或将参数引用到 shell 的情况。

此外,@ababert 已正确解释您不一定会PATH在 CGI 环境中正确设置,因此最佳做法是通过绝对路径名引用所有内容。同样,这通常是移植到不同平台和环境的好习惯(当然,硬编码二进制文件的路径已经妨碍了可移植性,但至少你可以最小化这个问题 - 谁知道你或其他人什么时候将需要移植此代码)。

考虑到这一点,您的ls示例echo将如下所示:

exit_status = subprocess.call(['/bin/ls'])
exit_status = subprocess.call(['/bin/echo', 'something'])

echo许多 shell 将其视为内置命令,它会覆盖实际echo命令——通过绝对路径(即/bin/echo)引用它会强制它使用实际命令而不是内置命令,这一事实也让人有些困惑。

当作为 CGI 脚本运行时,内部服务器错误通常意味着您有一个未捕获的 Python 异常。如果您查看网络服务器的日志文件(假设您可以访问它们),那么您应该找到异常的全文,但这确实因服务器而异。当没有运行时shell=True,我怀疑您在OSError找不到要运行的可执行文件时遇到异常。当你使用 shell 运行时,你不会得到异常——你只会得到 shell 错误消息的标准错误输出。同样,这应该在日志中重复。如果您确实遇到异常,我怀疑这是由于您正在运行的用户没有指定有效的登录 shell - 某些配置以用户身份运行脚本,例如/bin/false作为他们的登录外壳,以避免帐户被滥用。我希望返回代码1而不是127这种情况。

简而言之,查看您的网络服务器的错误日志中包含哪些信息,最佳做法是避免shell=True并确保将所有命令指定为参数列表而不是空格分隔的字符串。如果您可以从日志中获得更多信息并将其发布在此处,我们可以更新我们的答案以提供更具体的帮助。

最后,如果可能的话,我会避免fork()在网络服务器中使用任何代码,这包括subprocess. 有时没有办法避免它,但对于简单的事情,ls你可以使用 Python 函数代替(os.listdir())。当您作为标准 CGI 脚本运行时,您可能没问题,但如果您打算将代码移植到更高效的托管环境(例如 mod-wsgi、fastcgi),那么您必须非常小心生成外部进程,因为文件描述符和其他资源的重复发生。我并不是说它不能安全地完成,但它是未来令人困惑的错误的潜在来源,所以如果可以的话,我真的会避免它。更不用说生成外部进程来处理 Web 请求的相对低效率了,如果您计划扩展代码,这可能很重要。

于 2013-01-23T10:59:20.660 回答