PHP 的一个强大之处在于,可以很方便的在操作系统中执行 Shell 命令,所以基本上它就无所不能了,今天我们来了解下与之相关的内容。
PHP 支持一个执行运算符:反引号(``)。注意这不是单引号!PHP 会尝试将反引号中的内容作为 Shell 命令来执行,并将其输出信息返回(即,可以赋值给一个变量而不是简单的丢弃到标准输出)。使用反引号运算符的效果和函数 shell_exec()
是一样的。
反引号运算符在激活了「安全模式」或者禁用了
shell_exec()
时是无效的。和其它某些语言不通,反引号不能在双引号字符串中使用。
示例:
$cmd = `ls -al`;
echo $cmd;
/* 结果
total 16
drwxr-xr-x 2 git git 4096 Jun 26 00:28 .
drwxr-xr-x 14 root root 4096 Aug 17 17:16 ..
-rw-r--r-- 1 root root 52 Mar 22 08:39 .user.ini
-rw-r--r-- 1 git git 34 Aug 18 2018 index.php
*/
string escapeshellarg(string $arg)
把字符串转码为可以在 Shell 命令里使用的参数。
该函数会给字符串增加一个单引号并且能够引用或者转码任何已经存在的单引号,以确保能够将一个字符串传入 Shell 函数,并且还是安全的。对于用户输入的部分参数应该使用该函数。
示例:
system('ls ' . escapeshellarg($dir));
string escapeshellcmd(string $command)
对字符串中可能会欺骗 Shell 命令任意执行命令的字符进行转义。
反斜线(\)会在以下字符之前插入:&#;`/*?~<>^()[]{}$\,\x0A 和 \xFF。「'」和 「"」仅在不配对时被转义。在 Windows 平台上,这些字符以及 「%」 和 「!」 都会被空格代替。
该函数主要用于对完整的命令字符串进行转义。即使如此,攻击者还是可以传入任意数量的参数,所以在此之前还应用
escapeshellarg()
对单个参数进行转义。
string exec(string $command [, array &$output [, int &$return_var]])
执行 $command
参数所指定的命令。
如果提供了 $output
参数,那么会用命令执行的输出填充到此数组,每行输出填充数组中的一个元素。数组中的数据不包含行尾的空白字符,例如 \n
字符。注意,如果数组中已经包含了部分元素,exec()
函数会在数组末尾追加内容。如果不想追加,请在传入 exec()
函数之前,对数组进行 unset()
重置操作。
如果同时提供 $output
和 $return_var
参数,命令执行后的返回状态会被写入到此变量。
该函数返回执行结果的最后一行内容。如果要获取未经处理的全部输出数据,请使用 passthru()
函数。
为了能保持在后台运行,此函数必须将输出重定向到文件或其它输出流。否则会导致 PHP 挂起,直至程序执行结束。
Shell 命令中我们经常会看到
command > /dev/null 2>&1
这样的写法。这是什么意思呢。
>
表示重定向到哪里,例如:echo '123' > /home/file.txt1 表示 stdout 标准输出,系统默认值为 1,所以
>/dev/null
等同于1>/dev/null
2 表示 stderr 标准错误
&
表示等同于,2>&1
表示 2 的输出重定向和 1 一样
/dev/zero
代表一个永远输出 0 的设备文件,使用它作输出可以得到全为空的文件。因此可以用来创建新文件。例如使用 dd 命令创建一个 10Kb 大小的文件。dd if=/dev/zero of=file count=10 bs=1024
所以上面的意思就是,不输出任何标准输出,也不输出标准错误内容。
示例:
$cmd = 'ls -al';
$output = [];
$return_var = null;
echo exec($cmd, $output, $return_var);
print_r($output);
print_r($return_var);
/* 输出
-rw-r--r-- 1 git git 136 Aug 18 2018 index.php
Array
(
[0] => total 16
[1] => drwxr-xr-x 2 git git 4096 Jun 26 00:28 .
[2] => drwxr-xr-x 14 root root 4096 Aug 17 17:16 ..
[3] => -rw-r--r-- 1 root root 52 Mar 22 08:39 .user.ini
[4] => -rw-r--r-- 1 git git 136 Aug 18 2018 index.php
)
0
*/
void passthru(string $command [, int &$return_var])
执行外部程序并显示原始输出。
和 exec()
函数类似,passthru()
也是用来执行外部命令的。当所执行的 Unix 命令输出二进制数据,并且需要直接传输到浏览器的时候,需要用此函数来替代 exec()
和 system()
函数。常用来执行诸如 pbmplus 之类的可以直接输出图像流的命令。通过设置 Content-type 为 image/gif,然后调用 pbmplus 程序输出 gif 文件,就可以从 PHP 脚本中直接输出图像到浏览器。
示例:
// 下载文件
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"myfile.zip\"");
header("Content-Length: 11111");
passthru("cat myfile.zip",$err);
exit;
string shell_exec(string $cmd)
通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。
如果执行过程中发生错误或者进程不产生输出,则返回 NULL。所以,该函数无法通过返回值检测进程是否成功执行。如果需要检查进程执行的退出码,请使用 exec()
函数。
示例:
$output = shell_exec('ls -lart');
echo "$output";
/* 输出
total 16
-rw-r--r-- 1 root root 52 Mar 22 08:39 .user.ini
drwxr-xr-x 2 git git 4096 Jun 26 00:28 .
drwxr-xr-x 14 root root 4096 Aug 17 17:16 ..
-rw-r--r-- 1 git git 67 Aug 18 2018 index.php
*/
string system(string $command [, int &$return_var])
执行外部程序,并且显示输出。
和 C 版本的 system()
函数一样,该函数执行 $command 参数所指定的命令,并且输出执行结果。
如果 PHP 运行在服务器模块中,system()
函数还会尝试在每行输出完毕之后,自动刷新 web 服务器的输出缓存。