php使用exec导致结果叠加的坑

关于php在使用exec函数时结果叠加的坑

今天用php在后台跑一些常规任务的时候使用到了exec函数,它用来执行Linux系统函数进行相关统计,如exec("ls -l"),但是却发生了很诡异的问题,真的是特别诡异的问题,首先看下面这段代码

问题引入

$shell = 'date';

# 执行Linux下的 date 命令,输出当前日期
exec($shell, $result);
var_dump($result);

//结果如下:
array(1) {
  [0]=>
  string(42) "2016年 09月 29日 星期四 21:44:20 CST"
}

$shell = 'echo "1\n2\n3"';

exec($shell, $result);
var_dump($result);

// 按理说结果应该应该如下啊,exec会把shell执行的结果每一行作为一个数组元素最终赋值给$result
// 期望值
array(3) {
  [0]=>
  string(1) "1"
  [1]=>
  string(1) "2"
  [2]=>
  string(1) "3"
}

// 但最终结果却是下面的结果:
array(4) {
  [0]=>
  string(42) "2016年 09月 29日 星期四 21:44:20 CST"
  [1]=>
  string(1) "1"
  [2]=>
  string(1) "2"
  [3]=>
  string(1) "3"
}

你大概能够看明白发生什么了吧,对,exec会将结果追加$result中去!
每执行一次exec都会将新的结果追加进去!所以最终 $result 的结果并不是你想要的单纯的123,而是在之前 date 的基础上追加了123!

情景复现

所以,我当时依赖exec执行结果进行逻辑处理时,下面的代码是会出坑的

假设:

  1. 我们对shell输出结果进行处理和使用
  2. 执行的shell命令会把结果输出为一行,如 wc -l
  3. 那么exec的结果$result就是一个数组,并且只包含一个元素,就是输出的数据行
  4. 我们取$result[0]就是需要的shell命令执行结果
$shell = 'date';

for ($i = 0; $i < 3; $i++) {
    exec($shell, $result);
    sleep(1); // 模拟每次时间不同,shell结果不同

    // 这里是我们对输出结果进行处理和使用
    // 假设我们的shell会把结果输出为一行,打印到屏幕上
    // 那么 $result 数组应该就只包含一个元素,就是输出的数据行
    // 我们取 $result[0] 就是需要的结果

    var_dump($result[0]); // 假设这里是对结果的应用,后续依据这个进行其他操作
}

你知道结果是啥么?竟然是这个:


string(42) "2016年 09月 29日 星期四 22:02:26 CST"
string(42) "2016年 09月 29日 星期四 22:02:26 CST"
string(42) "2016年 09月 29日 星期四 22:02:26 CST"

为啥是一样的呢!!至少每个结果不一样,应该每个多一秒才对吧!!

过程分析

循环执行第一次时, $result是

array(3) {
  [0]=>
  string(42) "2016年 09月 29日 星期四 22:02:26 CST"
}

第二次时

array(3) {
  [0]=>
  string(42) "2016年 09月 29日 星期四 22:02:26 CST"
  [1]=>
  string(42) "2016年 09月 29日 星期四 22:02:27 CST"
}

第三次时

array(3) {
  [0]=>
  string(42) "2016年 09月 29日 星期四 22:02:26 CST"
  [1]=>
  string(42) "2016年 09月 29日 星期四 22:02:27 CST"
  [12=>
  string(42) "2016年 09月 29日 星期四 22:02:28 CST"
}

分析一下,就是因为exec每次将结果追加到 $result 中导致的,每次循环shell执行的结果都追加在 $result 的尾部,然后我们每次用 $result[0]作为预期数据时,实际上每次取的永远是第一次exec的结果,而不是我们想要的后面执行的而结果 囧~


解决方法

实际上 php.net 上官方已经明显的标注了,还是没认真读文档的锅

如果提供了 output 参数, 那么会用命令执行的输出填充此数组, 每行输出填充数组中的一个元素。 数组中的数据不包含行尾的空白字符,例如 \n 字符。 请注意,如果数组中已经包含了部分元素,exec() 函数会在数组末尾追加内容。如果你不想在数组末尾进行追加, 请在传入 exec() 函数之前 对数组使用 unset() 函数进行重置。

方法1

如果要多次使用exec的值,那么每次在exec之前清空$result即可

// 每次使用 $result 进行unset清空操作
unset($result);
exec($shell, $result);

方法2

但其实还有更简单的方法,直接用 shell_exec 也行,shell_exec不会有类似追加问题,直接将标准输出赋值到 $result 中

$result = shell_exec($shell);

觉得不错? 哈哈 感谢赏个键盘磨损钱~(左微信 右支付宝)

wechat 8.8 alipay 8.8