本文目录导读:

在PHP中操作Linux命令有多种方式,以下是常用的方法和最佳实践:
基础函数(按推荐程度排序)
exec() - 执行命令并返回最后一行
<?php
// 返回最后一行输出
$lastLine = exec('ls -la', $output, $returnCode);
echo "最后一行: " . $lastLine . PHP_EOL;
echo "完整输出: " . implode("\n", $output) . PHP_EOL;
echo "返回码: " . $returnCode;
?>
shell_exec() - 执行命令并返回完整输出
<?php
$output = shell_exec('ls -la');
echo "<pre>$output</pre>";
?>
system() - 直接输出结果
<?php
// 直接输出到浏览器
system('pwd', $returnCode);
echo "返回码: " . $returnCode;
?>
passthru() - 执行外部程序并显示原始输出
<?php
// 适合二进制输出
passthru('cat image.jpg', $returnCode);
?>
使用反引号运算符
<?php $output = `ls -la`; echo $output; ?>
proc_open() - 高级操作(推荐)
<?php
$descriptors = [
0 => ["pipe", "r"], // stdin
1 => ["pipe", "w"], // stdout
2 => ["pipe", "w"] // stderr
];
$process = proc_open('ls -la', $descriptors, $pipes);
if (is_resource($process)) {
// 读取标准输出
$stdout = stream_get_contents($pipes[1]);
// 读取错误输出
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
$returnCode = proc_close($process);
echo "标准输出: " . $stdout;
echo "错误输出: " . $stderr;
echo "返回码: " . $returnCode;
}
?>
安全最佳实践
转义参数
<?php
// 使用 escapeshellcmd 转义整个命令
$cmd = escapeshellcmd('ls -la');
// 使用 escapeshellarg 转义每个参数
$filename = $_GET['file']; // 用户输入
$cmd = 'cat ' . escapeshellarg($filename);
?>
验证用户输入
<?php
function safeSystemCall($command, $allowedCommands = []) {
// 验证命令
if (!in_array($command, $allowedCommands)) {
throw new Exception("不允许执行此命令");
}
// 执行并返回结果
return shell_exec($command);
}
// 使用示例
$allowed = ['ls', 'pwd', 'date'];
try {
echo safeSystemCall($_GET['cmd'], $allowed);
} catch (Exception $e) {
echo "错误: " . $e->getMessage();
}
?>
完整示例 - 执行命令并处理错误
<?php
function executeCommand($command, $timeout = 30) {
$descriptors = [
0 => ["pipe", "r"],
1 => ["pipe", "w"],
2 => ["pipe", "w"]
];
$process = proc_open(
$command,
$descriptors,
$pipes,
null,
null,
['timeout' => $timeout]
);
if (!is_resource($process)) {
return ['success' => false, 'error' => "无法启动进程"];
}
// 关闭stdin
fclose($pipes[0]);
// 读取输出(设置流超时)
stream_set_blocking($pipes[1], false);
$stdout = '';
$start = time();
while (true) {
$stdout .= fgets($pipes[1]);
if (time() - $start > $timeout) {
proc_terminate($process);
return ['success' => false, 'error' => "命令执行超时"];
}
if (feof($pipes[1])) {
break;
}
usleep(100000); // 100ms
}
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
$returnCode = proc_close($process);
return [
'success' => ($returnCode === 0),
'stdout' => $stdout,
'stderr' => $stderr,
'returnCode' => $returnCode
];
}
// 使用示例
$result = executeCommand('python3 script.py --input file.txt');
if ($result['success']) {
echo "执行成功:\n" . $result['stdout'];
} else {
echo "执行失败:\n" . $result['stderr'];
}
?>
使用 Symfony Process(推荐第三方库)
<?php
// 安装: composer require symfony/process
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
$process = new Process(['ls', '-la']);
$process->run();
if (!$process->isSuccessful()) {
throw new ProcessFailedException($process);
}
echo $process->getOutput();
?>
注意事项
-
安全警告:
- 永远不会直接执行用户输入的命令
- 使用白名单验证可执行命令
- 转义所有用户提供的参数
-
权限限制:
- Web服务器用户通常权限有限
- 使用sudo时需要配置NOPASSWD
-
错误处理:
- 始终检查返回码
- 处理stdout和stderr
-
性能考虑:
- 避免频繁执行系统命令
- 考虑使用缓存
-
环境问题:
- 某些PHP环境可能禁用exec类函数
- 检查php.ini中的disable_functions
根据具体需求选择合适的函数,推荐使用proc_open或Symfony Process以获得最佳控制。