运行外部程序的函数解析
PHP
提供了四个用来运行外部程序的函数——system、exec、passthru和shell_exec。 下面我们就这几个函数分别说明一下其用法,并且比较一下它们之间有什么不同。
system()
system ( string $command [, int &$return_var ] ) : string|false
system 接收两个参数:
第一个是要执行的外部命令;第二个参数是可选的。如果提供了第二个参数,则会将外部程序的执行完之后的状态码保存在第二个参数变量中(注意,是一个整数)。
当command正常执行成功,并且command有输出信息 则system函数返回值是输出信息的最后一行(对于cli模式下,command执行过程中产生的输出信息system都会显示到stdout)。 如果执行失败,则返回值为false
exec()
exec ( string $command [, array &$output [, int &$return_var ]] ) : string
exec 可以接收三个参数:第一个就是要执行的外部命令;第二、三个参数是可选的,如果提供了第二个参数,则会将command程序在执行的过程中产生的每一行数据以数组的形式保存到第二个参数中。 如果提供了第三个参数,则和system函数相同,将外部程序的执行完之后的状态码保存在第三个参数变量中。
当command正常执行成功,并且command有输出信息 则exec函数返回值是输出信息的最后一行。
和system函数不同的是,在command执行过程中产生的输出信息exec函数并不会显示在终端上。如果想要获取这些输出信息,可以通过提供第二个参数。当然也可以换用其他的函数,比如: system 或者 passthru。 要根据实际项目选择相应的函数。 如果command是一个自定义的服务程序,那么即使给exec提供了第二个参数,也不能在程序运行过程中获取到输出信息,这可能会影响自定义程序的使用。
passthru
passthru ( string $command [, int &$return_var ] ) : void
该函数和system一样接收两个参数,而且用途也都一样。 并且该函数也可以将command运行过程中产生的输出信息打印到终端上面。只是和system不同的是,输出信息是原生的。也就是说如果command输出二进制数据,则可以使用该函数。 除此之外,passthru是没有返回值的,这也是和system不同的地方。
shell_exec
shell_exec ( string $cmd ) : string|null
这个函数就比较简单,只有一个必须的参数。那就是我们要执行的命令。 并且在command执行的过程中产生的输出信息该函数也是不会显示到终端上的。 但是这些信息是会通过返回值返回给我们的主程序的。 如果程序执行错误或者程序没有输出的话,则返回null。
上面四个函数,在PHP底层都是使用的popen()
这个函数。关于这个函数这里不展开讨论,简单来说就是打开一个全双工的管道(pipe)。 我们的PHP主程序会在管道一端一直监听我们指定的外部程序是否有向管道发送信息(指定程序的运行状态,或者向终端的输出都会发送到管道中)。 其实也就是相当于两个进程通过管道进行通信。
如果外部程序就是一个服务程序。使用上面四个函数执行这个服务程序,这样就会一直有两个进程。PHP为主进程, 外部程序的进程是PHP主进程的子进程。 既然是两个进程,按说应该是并发的。因为服务程序是运行在子进程中的,这并不影响我们的PHP的主进程的运行。 这没错,事实也是我们的PHP主进程和子进程是并行的。然而我们的PHP主进程看起来像阻塞了一样,只有当子进程关闭以后,才会继续执行后面的PHP代码。 为什么会产生这样的现象,我们上面说了,popen()
函数是会打开一个管道和一个子进程。子进程和主进程通过这个管道进行通信。而我们的PHP进程是一直在监听这个管道,如果管道有信息,则把信息取出来进行相应的处理。也就是说只要子进程不退出,我们的PHP进程就一直会检查这个管道是否有信息。 这样看起来就像PHP进程被阻塞了一样。所以如果使用PHP运行一个服务程序,在处理的时候要注意这个问题。尤其是信号处理方面。