问题描述
C提供了标准功能system
,以使用 shell 运行子进程,许多语言也提供了类似的功能,例如AWK,Perl(带有单个参数)和PHP。有时,这些功能criticized不适合在security grounds上使用,或者由于外壳不是可移植的或is not the one used interactively而无法使用。
其他一些语言似乎也同意:它们仅提供一种无需外壳即可运行进程的方式,例如Java(标记任何单个字符串参数本身)和Tcl。 Python提供了direct wrapper和sophisticated replacement,它们可以避免使用外壳,并明确推荐(如does the user community)。
当然,对于许多应用程序来说,shell都是不必要的复杂性。运行外部进程会带来deadlock,orphan processes,退出状态不明确和file descriptor sharing的问题,在运行mkdir
或{{1}的情况下是不必要的}。但是,假设存在echo $VAR
是有原因的,那么何时使用它是正确的工具?
解决方法
对于C和Python(即使使用了适当的用例,也适合运行一个外部进程,尤其是通过外壳运行一个用例(不能像popen
那样过滤输出)。 实际 C system(3)
),还有其他警告。 POSIX specifies对于system
的其他行为:它忽略 SIGINT
和SIGQUIT
,并在执行期间阻塞SIGCHLD
。理由是,用户(可以从终端发送SIGINT
和SIGQUIT
的用户在执行过程中是interacting with the subprocess,而不是父母,并且system
必须处理SIGCHLD
的子进程,而不受应用程序的干扰。
这直接暗示了问题的答案:仅在以下情况下才使用system
- 用户直接要求执行特定的Shell命令(例如,
!
中带有less
),并且 - 应用程序无需对此这段时间内退出的任何 other 子进程做出反应(例如,它不应是多线程的)。
如果不满足#1的要求,则用户可能会发送一个终端信号,期望它杀死整个过程,并使它仅杀死(如果不是看不见的话,则是意外的)孩子。 Linux手册页特别警告using it in a loop,使用户无法中断。可能会注意到一个孩子已经退出并重新发出信号,但这是不可靠的,因为某些程序(例如,Python)exit
收到某些信号而不是重新提出,以指示退出原因以及外壳(由system
强制!)将退出状态与信号杀死状态进行合并。
在Python中,错误处理问题由于以下事实而更加复杂:os.system
遵循C退出状态(读取:错误代码)约定,而不是将失败报告为异常用户忽略孩子的退出状态。
答案很简单(从理论上讲),因为它与适用于许多其他编程问题的答案相同:当使程序员的工作更轻松并且用户的生活不再困难时,最好使用system()
。
但是,要想做到这一点,则需要做出大量判断,而且我们可能不会总是正确无误。但是,同样,编程中的许多判断调用都是如此。
由于大多数shell是用C编写的,因此没有原理 为什么没有system()
完成的任何事情就无法完成。但是,有时需要一堆完整的代码才能通过调用shell来完成一行代码。我猜popen()
也有同样的问题。
使用system()
引起了可移植性,线程安全性和信号管理问题。
不幸的是,我的经验是system()
给(程序员)带来最大好处的情况恰恰是它的可移植性最差的情况。
有时这样的担忧会建议使用不同的方法,有时它们并不重要-取决于应用程序。