PHP内置函数,提供了4种方法执行系统外部命令:

exec()、system()、passthru()、 shell_exec()

 

查看命令函数配置

在开始介绍前,先检查下php配置文件php.ini中是有禁止这是个函数

vim /etc/php.ini   或    vim /usr/local/php/lib/php.ini

php.ini 默认配置文件中是不禁止你调用执行外部命令的函数的。

找到 disable_functions,正常开启的配置为 disable_functions =  ,具体如下:

; This directive allows you to disable certain functions for security reasons.
; It receives a comma-delimited list of function names. This directive is
; *NOT* affected by whether Safe Mode is turned On or Off.
; http://php.net/disable-functions
disable_functions =

如果“disable_functions=”后面有接上面四个函数,将其删除,并重启 httpd 或  php-fpm

 

 

exec()

原型:string exec ( string $command [, array &$output [, int &$return_var ]] )

说明:

第一个参数, $command 是要执行的命令,例如: "ls -l"

第二个参数,$output 是command执行输出的结果,以数组格式存储

第三个参数,$return_var,用于保存command执行状态,0 - 成功,非零 - 失败

返回值,为command执行结果的最后一行

 

示例:

// exec
$retval = null;
$res = null;
$res_array = array();

$cmd = "/usr/bin/curl -k --head 'https://mimvp.com'";
$res = exec($cmd, $res_array, $retval);

echo "<br><br> exec result:";
var_dump($retval);		// 0 (返回的状态, 0 - 成功, 非零 - 失败)
var_dump($res);			// ''
var_dump($res_array);	// 返回结果数组

运行结果:

exec result:
/Users/homer/mimvp_site/api/header2.php:55:int 0
/Users/homer/mimvp_site/api/header2.php:56:string '' (length=0)
/Users/homer/mimvp_site/api/header2.php:57:
array (size=9)
  0 => string 'HTTP/2 200' (length=10)
  1 => string 'server: nginx' (length=13)
  2 => string 'date: Tue, 26 Dec 2017 01:52:11 GMT' (length=35)
  3 => string 'content-type: text/html; charset=UTF-8' (length=38)
  4 => string 'vary: Accept-Encoding' (length=21)
  5 => string 'vary: Accept-Encoding' (length=21)
  6 => string 'vary: mimvp-home-bj2' (length=20)
  7 => string 'vary: mimvp-homes-bj2' (length=21)
  8 => string '' (length=0)

从结果看

1)第二行结果 $retval 为状态 0 ,表示 cmd 命令执行成功

2)第三行结果 $res 为空字符串 '',表示 cmd 命令执行返回结果的最后一行,见 $res_array 数组最后一行(第八行)为空

3)第五行结果 $res_array 保存了完整的 cmd 命令执行结果,说明第二个参数保存 exec 执行结果

知识点:

exec 执行系统外部命令时,只返回结果的最后一行,如果想得到完整的执行结果,需要使用第二个参数,让其输出到指定的数组,此数组一个记录代表输出的一行,即如果输出结果有20行,则这个数组就有20条记录。所以,如果你需要反复输出调用不同系统外部命令的结果,最好在输出每一条系统外部命令结果时清空这个数组 $res_array = array(),以防混乱。第三个参数用来取得命令执行的状态码,通常执行成功都是返回0

 

 

system()

原型:string system ( string $command [, int &$return_var ] )

说明:

第一个参数, $command 是要执行的命令,例如: "ls -l"

第二个参数,$return_var,用于保存command执行状态,0 - 成功,非零 - 失败

返回值,为command执行结果的最后一行(system 和 exec 类似),失败返回 FALSE

 

示例:

// system
$retval = null;
$res = null;

$cmd = "/usr/bin/curl -k --head 'https://mimvp.com'";
$res = system($cmd, $retval);

echo "<br><br> system result:";
var_dump($retval);		// 0 (返回的状态, 0 - 成功, 非零 - 失败)
var_dump($res);			// ''

运行结果:

cmd : /usr/bin/curl -k --head 'https://mimvp.com'HTTP/2 200 server: nginx date: Tue, 26 Dec 2017 01:52:11 GMT content-type: text/html; charset=UTF-8 vary: Accept-Encoding vary: Accept-Encoding vary: mimvp-home-bj2 vary: mimvp-homes-bj2 

system result:
/Users/homer/mimvp_site/api/header2.php:41:int 0
/Users/homer/mimvp_site/api/header2.php:42:string '' (length=0)

从结果看

1)第一行结果,打印输出命令时,直接在屏幕上打印了 sytem 执行命令的返回结果,没法保存,因此system用于不保存结果的场景

2)第三行结果 $retval 为状态 0 ,表示 cmd 命令执行成功

3)第四行结果 $res 为空字符串 '',表示 cmd 命令执行返回结果的最后一行,其结果为空

 

 

passthru()

原型:void passthru ( string $command [, int &$return_var ] )

passthru 和 system 基本是一样的,结果不保存,直接输出在屏幕上,两者仅是返回值不同

1)system 是返回结果的最后一行,类型为 string

2)passthru 不返回结果,类型为 void

说明:

第一个参数, $command 是要执行的命令,例如: "ls -l"

第二个参数,$return_var,用于保存command执行状态,0 - 成功,非零 - 失败

返回值,为空 void

 

示例:

// passthru
$retval = null;
$res = null;

$cmd = "/usr/bin/curl -k --head 'https://mimvp.com'";
$res = passthru($cmd, $retval);

echo "<br><br> passthru result:";
var_dump($retval);		// 0 (返回的状态, 0 - 成功, 非零 - 失败)
var_dump($res);			// ''

运行结果:

HTTP/2 200 server: nginx date: Tue, 26 Dec 2017 01:52:11 GMT content-type: text/html; charset=UTF-8 vary: Accept-Encoding vary: Accept-Encoding vary: mimvp-home-bj2 vary: mimvp-homes-bj2 

passthru result:
/Users/homer/mimvp_site/api/header2.php:48:int 0
/Users/homer/mimvp_site/api/header2.php:49:null

从结果看

1)第一行结果,打印输出命令时,直接在屏幕上打印了 sytem 执行命令的返回结果,没法保存,跟system类似

2)第三行结果 $retval 为状态 0 ,表示 cmd 命令执行成功

3)第四行结果 $res 为空字符串为null,即不返回结果 void

 

 

shell_exec()

原型:string shell_exec ( string $cmd )

shell_exec 与 exec 类似,只是没有返回状态,其状态和cmd执行结果,都以结果返回

1)如果执行cmd成功,则返回执行结果

2)如果执行cmd失败,则返回 NULL

说明:

第一个参数, $command 是要执行的命令,例如: "ls -l"

返回值,为command执行结果,如果失败则返回 NULL

 

示例:

// shell_exec
$res = null;

$cmd = "/usr/bin/curl -k --head 'https://mimvp.com'";
$res = shell_exec($cmd);

echo "<br><br> shell_exec result:";
var_dump($res);			// 返回执行结果,或失败返回NULL

运行结果:

shell_exec result:
/Users/homer/mimvp_site/api/header2.php:84:string 'HTTP/2 200 
server: nginx
date: Tue, 26 Dec 2017 01:52:11 GMT
content-type: text/html; charset=UTF-8
vary: Accept-Encoding
vary: Accept-Encoding
vary: mimvp-home-bj2
vary: mimvp-homes-bj2

' (length=198)

从结果看

执行cmd输出结果,以字符串返回给了 $res ,并保留了换行

 

 

popen()

原型:resource popen ( string $command , string $mode )

通过pipe管道执行shell命令,也是一种常见的方式,python也有类似的命令

说明:

第一个参数, $command 是要执行的命令,例如: "ls -l"

第二个参数,$mode 是指定读写、执行等操作类型,如 'r', 'w', 'x'

返回值,是一个资源句柄,可用来具体的操作命令的资源,一般配合 fgets()、fgetss()、fwrite() 使用

 

示例:

// popen
$handle = popen($cmd, 'r');
$res = fread($handle, 8888);
echo "<br><br> popen result:";
var_dump($res);		
pclose($handle);	// 资源句柄执行完后,一定要记得关闭

运行结果:

popen result:
/Users/homer/mimvp_site/api/header2.php:90:string 'HTTP/2 200 
server: nginx
date: Tue, 26 Dec 2017 02:57:55 GMT
content-type: text/html; charset=UTF-8
vary: Accept-Encoding
vary: Accept-Encoding
vary: mimvp-home-bj2
vary: mimvp-homes-bj2

' (length=198)

 

 

PHP 执行命令函数的应用实例

PHP 执行外部命令函数 exec()、system()、passthru()、shell_exec() 不是花瓶,一切皆因需求而生

例如,PHP 前端执行直接从shell获取一些查询结果,充分利用强大的shell命令,如 curl、wget、whois 等

 

PHP 内置函数 get_headers() 用于获取服务器端返回的头信息,如果失败了可以通过 curl --head 来获取

实例代码,封装如下

// 获取server返回头信息
function get_response_header($url='https://mimvp.com') {
	$res_headers = array();
	
	try {
		$res_headers = get_headers($url, 1);		// 0 - 不格式化; 1 - 格式化
		if($res_headers == false) {	// 返回结果失败
			
			$retval = null;
			$res = null;
			$res_array = array();
			$cmd = sprintf("/usr/bin/curl -k --head '%s'", $url);
			echo '<br><br> cmd : ' . $cmd;
			
			// exec
			$res = exec($cmd, $res_array, $retval);
			echo "<br><br> exec result:";
			var_dump($retval);		// 0 (返回的状态, 0 - 成功, 非零 - 失败)
			var_dump($res);			// ''
			var_dump($res_array);	// 返回结果数组
			
			$idx = 0;
			foreach($res_array as $line) {			// $res_array 转成 $res_headers
				$line = str_replace(":", ":", trim($line));
				
				if($idx == 0 && $line != "") {		// 处理第一行的状态
					$res_headers[0] = $line;
				}
				if(strstr($line, ":") != "") {
					$key = trim(explode(":", $line)[0]);
					$val = trim(explode(":", $line)[1]);
					$res_headers[$key] = $val;
				}
				else if($idx >= 2 && $line != "") {
					$res_headers[$idx] = $line;
				}
				$idx += 1;
			}
		}
	} catch (Exception $e) {
		var_dump($e);
	}
	
	var_dump($res_headers);
	return $res_headers;
}

运行结果

1)正常时,执行 get_headers() ,返回结果如下:

/Users/homer/Downloads/myCode/workspace/navi_client/api/header2.php:67:
array (size=6)
  0 => string 'HTTP/1.1 200 OK' (length=15)
  'Server' => string 'nginx' (length=5)
  'Date' => string 'Tue, 26 Dec 2017 02:38:59 GMT' (length=29)
  'Content-Type' => string 'text/html; charset=UTF-8' (length=24)
  'Connection' => string 'close' (length=5)
  'Vary' => 
    array (size=4)
      0 => string 'Accept-Encoding' (length=15)
      1 => string 'Accept-Encoding' (length=15)
      2 => string 'mimvp-home-bj2' (length=14)
      3 => string 'mimvp-homes-bj2' (length=15)

 

2)失败时,执行 curl --head ,返回结果如下:

/Users/homer/Downloads/myCode/workspace/navi_client/api/header2.php:67:
array (size=5)
  0 => string 'HTTP/2 200' (length=10)
  'server' => string 'nginx' (length=5)
  'date' => string 'Tue, 26 Dec 2017 02' (length=19)
  'content-type' => string 'text/html; charset=UTF-8' (length=24)
  'vary' => string 'mimvp-homes-bj2' (length=15)

在 shell 执行cmd命令,查看其结果

$ /usr/bin/curl -k --head 'https://mimvp.com'
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 26 Dec 2017 02:45:52 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
Vary: Accept-Encoding
Vary: mimvp-home-bj2
Vary: mimvp-homes-bj2

可见,curl --head 基本补充了 get_headers 执行失败时获取服务器端头信息的功能

还有一点瑕疵就是 Vary 变量数组处理的不完美,完整代码量大,在此不给出了,请自行判断实现之