PHP 命令行工具 shell_exec, exec, passthru, system详细使用介绍


Posted in PHP onSeptember 11, 2011

所有这些命令都衍生一个子进程,用于运行您指定的命令或脚本,并且每个子进程会在命令输出写到标准输出 (stdout) 时捕捉它们。

shell_exec()

shell_exec() 命令行实际上仅是反撇号 (`) 操作符的变体。如果您编写过 shell 或 Perl 脚本,您就知道可以在反撇号操作符内部捕捉其他命令的输出。例如,清单 1 显示了如何使用反撇号在当前目录中获取每个文本(.txt)的单词计数。

清单 1. 使用反撇号计算单词数量

#! /bin/sh 
number_of_words=`wc -w *.txt` 
echo $number_of_words #result would be something like: 
#165 readme.txt 388 results.txt 588 summary.txt 
#and so on....

在您的 PHP 脚本中,您可以在 shell_exec() 中运行这个简单的命令,如清单 2 所示,并获取想要的结果。这里假设在同一个目录下有一些文本文件。

清单 2. 在 shell_exec() 中运行相同的命令

<?php 
$results = shell_exec('wc -w *.txt'); 
echo $results; 
?>

在图 1 中可以看到,获得的结果与从 shell 脚本得到的一样。这是因为 shell_exec() 允许您通过 shell 运行外部程序,然后以字符串的形式返回结果。
图 1. 通过 shell_exec() 运行 shell 命令的结果
PHP 命令行工具 shell_exec, exec, passthru, system详细使用介绍
注意,仅使用后撇号操作符也会得到相同的结果,如下所示。
清单 3. 仅使用后撇号操作符
<?php 
$results = `wc -w *.txt`; 
echo $results; 
?>

清单 4 给出了一种更加简单的方法。
清单 4. 更加简单的方法
<?php 
echo `wc -w *.txt`; 
?>

通过 UNIX 命令行和 shell 脚本能够完成很多东西,知道这点很重要。例如,您可以使用竖线将命令连接起来。您甚至可以使用操作符在其中创建 shell 脚本,并且仅调用 shell 脚本(根据需要使用或不使用参数)。

例如,如果您仅希望计算该目录下的前 5 个文本文件的单词数,那么可以使用竖线 (|) 将 wc 和 head 命令连接起来。另外,您还可以将输出结果放到 pre 标记内部,让它能够更美观地呈现在 Web 浏览器中,如下所示。

清单 5. 更加复杂的 shell 命令

<?php 
$results = shell_exec('wc -w *.txt | head -5'); 
echo "<code lang="php">".$results . "</code>"; 
?>

图 2 演示了运行清单 5 的脚本得到的结果。
图 2. 从 shell_exec() 运行更复杂的 shell 命令得到的结果
PHP 命令行工具 shell_exec, exec, passthru, system详细使用介绍
在本文的后面部分,您将学习如何使用 PHP 为这些脚本传递参数。现在您可以将它看作运行 shell 命令的一种方法,但要记住您只能看到标准输出。如果命令或脚本出现错误,您将看不到标准的错误 (stderr),除非您通过竖线将它添加到 stdout。

passthru()

passthru() 允许您运行外部程序,并在屏幕上显示结果。您不需要使用 echo 或 return 来查看结果;它们会显示在浏览器上。您可以添加可选的参数,即保存从外部程序返回的代码的变量,比如表示成功的 0,这为调试提供更好的机制。

在清单 6 中,我使用 passthru() 命令运行在前面小节运行的单词计数脚本。如您所见,我还添加一个包含返回代码的 $returnval 变量。

清单 6. 使用 passthru() 命令运行单词计数脚本

<?php 
passthru('wc -w *.txt | head -5',$returnval); 
echo "<hr/>".$returnval; 
?>

注意,我不需要使用 echo 返回任何东西。结果会直接显示在屏幕上,如下所示。
图 3. 使用 return 代码运行 passthru() 命令的结果
PHP 命令行工具 shell_exec, exec, passthru, system详细使用介绍
在清单 7 中,我通过删除脚本头部的 5 前面的短横线 (-) 引入一个小错误。
清单 7. 在单词计数脚本中引入一个错误
<?php 
//we introduce an error below (removing - from the head command) passthru('wc -w *.txt | head 5',$returnval); 
echo "<hr/>".$returnval; 
?>

注意,脚本未能按照预期运行。您得到的是一个空白的屏幕,一条水平线和返回值 1,如图 4 所示。这个返回代码通常表明发生了某些错误。如果能够测试返回代码,查找和修复错误就容易多了。
图 4. 使用 passthru() 时查看错误代码
PHP 命令行工具 shell_exec, exec, passthru, system详细使用介绍
exec()
exec() 命令与 shell_exec() 相似,不同之处是它返回输出的最后一行,并且可选地用命令的完整输出和错误代码填充数组。清单 8 展示了当运行 exec() 而不捕捉数据数组中的数据时发生的事情。
清单 8. 运行 exec() 而不捕捉数据数组中的数据
<?php 
$results = exec('wc -w *.txt | head -5'); 
echo $results; 
#would print out just the last line or results, i.e.: 
#3847 myfile.txt 
?>

为了捕捉数组中的结果,要将该数组的名称作为第二个参数添加到 exec()。我在清单 9 中执行了这个步骤,并以 $data 作为数组的名称。
清单 9. 从 exec() 捕捉数据数组的结果
<?php 
$results = exec('wc -w *.txt | head -5',$data); 
print_r($data); 
#would print out the data array: 
#Array ( [0]=> 555 text1.txt [1] => 283 text2.txt) 
?>

在捕捉数组中的结果之后,您可以对每行进行一些处理。例如,您可以在第一个空格处进行划分,将分离的值存储在数据库表中,或对每个行应用特定的格式或标记。
system()
如清单 10 所示,system() 命令是一种混合体。它像 passthru() 一样直接输出从外部程序接收到的任何东西。它还像 exec() 一样返回最后一行,并使返回代码可用。
清单 10. system() 命令
<?php 
system('wc -w *.txt | head -5'); 
#would print out: 
#123 file1.txt 332 file2.txt 444 file3.txt 
#and so on 
?>

一些例子
现在您已经了解如何使用这些 PHP 命令,但可能仍然有一些疑问。例如,什么时候应该使用哪个命令?这完全由您的需求决定。
大多数情况下,我使用 exec() 命令和数据数组处理所有东西。或者对更简单的命令使用 shell_exec(),尤其是不关心结果时。如果仅需返回一个 shell 脚本,我就使用 passthru()。通常,我在不同的场合中使用不同的函数,并且有时它们是可以互换的。这完全取决于我的心情和要实现的目的。
您可能提问的另一个问题是 “它们的长处是什么?”。如果您没有头绪,或者一个项目非常适合使用 shell 命令,但不知道如何使用,那么我在这里提供一些见解。
如果您正在编写一个提供各种备份或文件传输功能的应用程序,您可以选择使用 shell_exec() 或这里提供的其他命令之一运行 rsync 支持的 shell 脚本。您可以编写 shell 脚本使其包含必要的 rsync 命令,然后使用 passthru() 根据用户的命令或 cron 作业执行它。
例如,一位用户在您的应用程序中有适当的权限(比如管理员权限),他想将 50 个 PDF 文件从一个服务器发送到另一个服务器。那么,该用户需要在应用程序中导航到正确的位置,单击 Transfer,选择需要发送的 PDF,然后单击 Submit。在这个过程中,该表单应该有一个 PHP 脚本,它使用返回选项变量通过 passthru() 运行 rsync 脚本,这样您就知道是否发生问题,如下所示。
清单 11. 通过 passthru() 运行 rsync 脚本的示例 PHP 脚本
<?php 
passthru('xfer_rsync.sh',$returnvalue); 
if ($returnvalue != 0){ 
//we have a problem! 
//add error code here 
}else{ 
//we are okay 
//redirect to some other page 
} 
?>

如果您的应用程序需要列出进程或文件,或关于这些进程或文件的数据,您可以使用本文总结的命令之一轻松实现这个目的。例如,一个简单的 grep 命令能够帮助您找到匹配特定搜索条件的文件。将它与 exec() 命令一起使用可以将结果保存到一个数组中,这允许您构建一个 HTML 表或表单,它们又进一步允许您运行其他命令。
到目前为止,我讨论了用户生成的事件 —— 用户只要按下按钮或单击链接,PHP 就运行相应的脚本。您还可以将独立的 PHP 脚本和 cron 或其他日程安排程序一起使用,从而实现一些有趣的效果。例如,如果您一个备份脚本,您可以通过 cron 运行它,或者将它打包到 PHP 脚本后在运行。为什么要这样做?这似乎是多余的,不是吗?不是这样的 —— 您需要这样考虑,您可以通过 exec() 或 passthru() 运行备份脚本,然后根据返回代码执行一些行为。如果出现错误,您可以将其记录到错误日志或数据库中,或发送一封警告电子邮件。如果脚本成功,您可以将原始的输出转储到数据库(例如,rsync 有一个详尽(verbose)模式,对随后诊断问题十分有用)。

--------------------------------------------------------------------------------
安全
我们在这里简要讨论一下安全性:如果您接受用户输入并将信息传递到 shell,那么最好过滤用户输入。删除您认为有害的命令和不允许的内容,比如 sudo(作为超级用户运行)或 rm(删除)。事实上,您可能不希望用户发送开放的请求,而是让他们从列表中选择。
例如,您运行一个接受文件列表作为参数的传输程序,您应该通过一系列复选框列出所有文件。用户可以选择和取消选择文件,并通过单击 Submit 激活 rsync shell 脚本。用户不能自己输入文件或使用正则表达式。

--------------------------------------------------------------------------------
结束语
在本文中,我演示了使用 PHP 命令运行 shell 脚本和其他命令的基础知识。这些 PHP 命令包括 shell_exec()、exec()、passthru() 和 system()。现在,您应该在自己的应用程序中实践学到的知识。

PHP 相关文章推荐
PHP中的加密功能
Oct 09 PHP
php遍历目录viewDir函数
Dec 15 PHP
编写安全 PHP应用程序的七个习惯深入分析
Jun 08 PHP
PHP error_log()将错误信息写入一个文件(定义和用法)
Oct 25 PHP
PHP return语句的另一个作用
Jul 30 PHP
Yii框架form表单用法实例
Dec 04 PHP
PHP正则表达式之捕获组与非捕获组
Nov 06 PHP
Symfony2学习笔记之模板用法详解
Mar 17 PHP
php函数mkdir实现递归创建层级目录
Oct 27 PHP
PHP未登录自动跳转到登录页面
Dec 21 PHP
PHP单例模式应用示例【多次连接数据库只实例化一次】
Dec 18 PHP
php7 list()、session及其他模块的修改实例分析
May 25 PHP
20个PHP常用类库小结
Sep 11 #PHP
php各种编码集详解和以及在什么情况下进行使用
Sep 11 #PHP
php正则表达式(regar expression)
Sep 10 #PHP
PHP setcookie指定domain参数后,在IE下设置cookie失效的解决方法
Sep 09 #PHP
判断PHP数组是否为空的代码
Sep 08 #PHP
PHP中通过语义URL防止网站被攻击的方法分享
Sep 08 #PHP
PHP session会话的安全性分析
Sep 08 #PHP
You might like
关于php微信订阅号开发之token验证后自动发送消息给订阅号但是没有消息返回的问题
2015/12/21 PHP
thinkPHP5框架实现多数据库连接,跨数据连接查询操作示例
2019/05/29 PHP
php创建多级目录与级联删除文件的方法示例
2019/09/12 PHP
Laravel 添加多语言提示信息的方法
2019/09/29 PHP
laravel orm 关联条件查询代码
2019/10/21 PHP
基于jquery的checkbox下拉框插件代码
2010/06/25 Javascript
类似php的js数组的in_array函数自定义方法
2013/12/27 Javascript
js通过元素class名字获取元素集合的具体实现
2014/01/06 Javascript
jQuery实现淡入淡出二级下拉导航菜单的方法
2015/08/28 Javascript
使用CDN和AJAX加速WordPress中jQuery的加载
2015/12/05 Javascript
js 求时间差的实现代码
2016/04/26 Javascript
ionic在开发ios系统微信时键盘挡住输入框的解决方法(键盘弹出问题)
2016/09/06 Javascript
vue实现可增删查改的成绩单
2016/10/27 Javascript
javascript ASCII和Hex互转的实现方法
2016/12/27 Javascript
canvas红包照片实例分享
2017/02/28 Javascript
jquery获取transform里的值实现方法
2017/12/12 jQuery
基于IView中on-change属性的使用详解
2018/03/15 Javascript
js调用网络摄像头的方法
2020/12/05 Javascript
python3访问sina首页中文的处理方法
2014/02/24 Python
Android模拟器无法启动,报错:Cannot set up guest memory ‘android_arm’ Invalid argument的解决方法
2016/07/01 Python
利用Python开发实现简单的记事本
2016/11/15 Python
Python中使用多进程来实现并行处理的方法小结
2017/08/09 Python
基于Python __dict__与dir()的区别详解
2017/10/30 Python
Selenium(Python web测试工具)基本用法详解
2018/08/10 Python
Win10下安装并使用tensorflow-gpu1.8.0+python3.6全过程分析(显卡MX250+CUDA9.0+cudnn)
2020/02/17 Python
英国电动工具购买网站:Anglia Tool Centre
2017/04/25 全球购物
Bally澳大利亚官网:瑞士奢侈品牌
2018/11/01 全球购物
俄罗斯极限运动网上商店:Board Shop №1
2020/12/18 全球购物
.NET是怎么支持多种语言的
2015/02/24 面试题
自荐信要包含哪些内容
2013/11/06 职场文书
法定代表人授权委托书范文
2014/08/02 职场文书
展览会邀请函
2015/02/02 职场文书
地震慰问信
2015/02/14 职场文书
人事任命书范本
2015/09/21 职场文书
Matlab求解数组中的最大值及它所在的具体位置
2021/04/16 Python
5分钟教你docker安装启动redis全教程(全新方式)
2021/05/29 Redis