为什么popen()会调用shell来执行进程?

时间:2018-02-20 11:36:57

标签: c linux sh popen

我目前正在阅读并试验在Linux上使用C代码运行程序的各种可能性。我的用例涵盖了所有可能的场景,从简单地运行和忘记一个过程,阅读写入过程,到阅读写入它。

对于前三个,popen()非常易于使用且效果很好。我知道它在内部使用了某个版本的fork()exec(),然后调用shell来实际运行命令。

对于第三种情况,popen()不是一个选项,因为它是单向的。可用选项包括:

  • 手动fork()exec(),加上pipe()dup2()输入/输出
  • posix_spawn(),内部使用上述内容作为需要

我注意到这些可以达到与popen()相同的效果,但我们可以完全避免调用额外的sh。这听起来很可取,因为它似乎不太复杂。

但是,我注意到,即使我在互联网上发现的examples on posix_spawn()确实调用了一个shell,所以它似乎必然会带来好处。如果它是关于解析命令行参数,wordexp()似乎也做得同样好。

调用shell运行所需进程而不是直接运行它的好处背后的原因是什么?

编辑:我意识到我对这个问题的措辞并没有完全反映我的实际兴趣 - 我对通过{{ 好处更加好奇1}}而不是(历史)原因,虽然两者明显相关,但两种变体的答案同样相关。

3 个答案:

答案 0 :(得分:5)

调用shell允许您执行在shell中可以执行的所有操作。 例如,

Chrome
使用FILE *fp = popen("ls *", "r"); 可以

(展开当前目录中的所有文件)。 将其与:

进行比较
popen()

您不能以execvp("/bin/ls", (char *[]){"/bin/ls", "*", NULL}); 作为参数执行ls,因为*将按字面解释exec(2)

同样,*可以使用管道(|),重定向(><,......)等。

否则,如果您不需要shell,则没有理由使用popen - 这是不必要的。你最终会得到一个额外的shell进程,所有在shell中出错的东西都可能在你的程序中出错(例如,你传递的命令可能被shell错误地解释并且存在一个常见的安全问题)。 popen() is designed that waypopen + fork解决方案更清晰,没有与shell相关的问题。

答案 1 :(得分:3)

滑稽的答案是因为POSIX标准(http://pubs.opengroup.org/onlinepubs/9699919799/functions/popen.html)这样说。或者更确切地说,它表示它的行为应该像命令参数传递给/ bin / sh进行解释一样。

所以我认为一致的实现原则上也可以有一些内部库函数来解释shell命令,而不必分叉和执行单独的shell进程。我实际上并没有意识到任何这样的实现,我怀疑所有的角落情况都是正确的。

答案 2 :(得分:2)

The 2004 version of the POSIX system() documentation的基本原理也可能适用于popen()。请注意system()所述的限制,尤其是说明进程ID不同的一个&#34;:

  

<强> 基本原理

     

...

     

system()函数有三个级别的规范。该   ISO C标准给出了最基本的标准。它需要这个功能   存在,并为应用程序定义一种查询方式   命令语言解释器存在。它没有说明命令   语言或解释命令的环境。

     

IEEE Std 1003.1-2001对system()施加了额外的限制。它   要求如果有命令语言解释器,   环境必须是fork()和exec指定的。这确保了   例如,close-on-exec有效,文件锁不会被继承,   并且进程ID不同。它还指定了回报   当命令行可以运行时,来自system()的值,从而给出   申请一些关于命令完成状态的信息。

     

最后,IEEE Std 1003.1-2001要求解释命令   与Shell和Utilities中定义的shell命令语言一样   IEEE Std 1003.1-2001的数量。

请注意对&#34; ISO C标准&#34;的多次引用。 The latest version of the C standard要求命令字符串由系统&#34;命令处理器&#34;处理:

  

7.22.4.8 system功能

     

概要

#include <stdlib.h>
int system(const char *string);
     

<强>描述

     

如果string是空指针,则system函数确定   主机环境是否有命令处理器。如果string   不是空指针,system函数传递字符串   string指向要执行的命令处理器   以实施方式记录的方式;这可能会   导致调用system的程序在不符合的情况下运行   方式或终止。

     

<强>返回

     

如果参数是空指针,则为system函数   仅当命令处理器可用时才返回非零值。如果   参数不是空指针和system函数   确实返回,它返回一个实现定义的值。

由于C标准要求系统&#34;命令处理器&#34;用于system()电话,我怀疑:

  1. 在某个地方,POSIX需要将popen()system()实施联系起来。
  2. 只需重复使用&#34;命令处理器&#34;完全是因为还需要作为一个单独的过程运行。
  3. 所以这是两次删除的滑稽答案。