本文目录导读:

- 方案一:使用 phpseclib 库(纯PHP实现,无需SSH2扩展)
- 方案二:使用 SSH2 扩展(性能更好)
- 方案三:并行执行(提高效率)
- 方案四:使用 Symfony Process 组件(推荐生产环境)
- 结果汇总与展示
- 推荐使用场景
- 建议
在PHP项目中批量执行SSH命令并汇总结果,常见的有以下几种方案:
使用 phpseclib 库(纯PHP实现,无需SSH2扩展)
安装
composer require phpseclib/phpseclib:~3.0
批量执行示例
<?php
require_once 'vendor/autoload.php';
use phpseclib3\Net\SSH2;
class SSHCommandExecutor
{
private $results = [];
public function executeBatch($servers, $commands)
{
foreach ($servers as $index => $server) {
$ssh = new SSH2($server['host'], $server['port'] ?? 22);
if (!$ssh->login($server['username'], $server['password'])) {
$this->results[] = [
'server' => $server['host'],
'status' => 'failure',
'error' => 'Login failed'
];
continue;
}
$serverResults = [];
foreach ($commands as $cmd) {
$output = $ssh->exec($cmd);
$serverResults[] = [
'command' => $cmd,
'output' => $output,
'exit_code' => $ssh->getExitStatus()
];
}
$this->results[] = [
'server' => $server['host'],
'status' => 'success',
'commands' => $serverResults
];
$ssh->disconnect();
}
return $this->results;
}
public function summarizeResults()
{
$summary = [
'total_servers' => count($this->results),
'successful' => 0,
'failed' => 0,
'server_details' => []
];
foreach ($this->results as $result) {
if ($result['status'] === 'success') {
$summary['successful']++;
} else {
$summary['failed']++;
}
$summary['server_details'][] = $result;
}
return $summary;
}
}
// 使用示例
$executor = new SSHCommandExecutor();
$servers = [
['host' => '192.168.1.100', 'username' => 'root', 'password' => 'pass1'],
['host' => '192.168.1.101', 'username' => 'root', 'password' => 'pass2']
];
$commands = [
'uptime',
'free -h',
'df -h'
];
$results = $executor->executeBatch($servers, $commands);
$summary = $executor->summarizeResults();
// 输出结果
echo json_encode($summary, JSON_PRETTY_PRINT);
使用 SSH2 扩展(性能更好)
<?php
class SSH2BatchExecutor
{
private $connections = [];
private $results = [];
public function connectBatch($servers)
{
foreach ($servers as $key => $server) {
$connection = ssh2_connect($server['host'], $server['port'] ?? 22);
if (!$connection) {
$this->results[$key] = [
'status' => 'failure',
'error' => 'Connection failed'
];
continue;
}
if (!ssh2_auth_password($connection, $server['username'], $server['password'])) {
$this->results[$key] = [
'status' => 'failure',
'error' => 'Authentication failed'
];
continue;
}
$this->connections[$key] = $connection;
}
}
public function executeCommands($commands)
{
foreach ($this->connections as $key => $connection) {
$commandResults = [];
foreach ($commands as $cmd) {
$stream = ssh2_exec($connection, $cmd);
stream_set_blocking($stream, true);
$stdout = stream_get_contents($stream);
fclose($stream);
// 获取stderr
$stderr = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
$errorOutput = stream_get_contents($stderr);
fclose($stderr);
$commandResults[] = [
'command' => $cmd,
'stdout' => $stdout,
'stderr' => $errorOutput
];
}
$this->results[$key] = [
'server' => $key,
'status' => 'success',
'commands' => $commandResults
];
}
return $this->results;
}
public function generateReport($format = 'table')
{
$report = [];
foreach ($this->results as $server => $result) {
if ($result['status'] === 'success') {
foreach ($result['commands'] as $cmdResult) {
$report[] = [
'server' => $server,
'command' => $cmdResult['command'],
'status' => 'success',
'output' => $cmdResult['stdout']
];
}
} else {
$report[] = [
'server' => $server,
'command' => 'N/A',
'status' => 'failed',
'output' => $result['error']
];
}
}
return $report;
}
public function disconnectAll()
{
foreach ($this->connections as $connection) {
ssh2_disconnect($connection);
}
}
}
并行执行(提高效率)
<?php
class ParallelSSHExecutor
{
private $results = [];
public function executeParallel($servers, $commands, $callback = null)
{
$processes = [];
$pipes = [];
foreach ($servers as $index => $server) {
$command = sprintf(
'sshpass -p %s ssh -o StrictHostKeyChecking=no %s@%s "%s"',
escapeshellarg($server['password']),
escapeshellarg($server['username']),
escapeshellarg($server['host']),
escapeshellarg(implode(' && ', $commands))
);
$processes[$index] = proc_open(
$command,
[
0 => ['pipe', 'r'],
1 => ['pipe', 'w'],
2 => ['pipe', 'w']
],
$pipes[$index]
);
}
// 读取结果
foreach ($processes as $index => $process) {
$stdout = stream_get_contents($pipes[$index][1]);
$stderr = stream_get_contents($pipes[$index][2]);
fclose($pipes[$index][0]);
fclose($pipes[$index][1]);
fclose($pipes[$index][2]);
$exitCode = proc_close($process);
$result = [
'server' => $servers[$index]['host'],
'stdout' => $stdout,
'stderr' => $stderr,
'exit_code' => $exitCode
];
$this->results[] = $result;
if ($callback) {
$callback($result);
}
}
return $this->results;
}
}
使用 Symfony Process 组件(推荐生产环境)
<?php
require_once 'vendor/autoload.php';
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
class SymfonySSHExecutor
{
private $results = [];
public function executeWithProcessBuilder($servers, $commands)
{
$processes = [];
foreach ($servers as $index => $server) {
$sshCommand = sprintf(
'ssh %s@%s "%s"',
$server['username'],
$server['host'],
implode(' && ', $commands)
);
$process = new Process(['bash', '-c', $sshCommand]);
$process->setTimeout(3600);
$process->setIdleTimeout(60);
$processes[$index] = $process;
}
// 并行执行
foreach ($processes as $index => $process) {
try {
$process->mustRun();
$this->results[] = [
'server' => $servers[$index]['host'],
'status' => 'success',
'output' => $process->getOutput(),
'error_output' => $process->getErrorOutput()
];
} catch (ProcessFailedException $exception) {
$this->results[] = [
'server' => $servers[$index]['host'],
'status' => 'failure',
'error' => $exception->getMessage()
];
}
}
return $this->results;
}
public function exportToCSV($filename = 'ssh_results.csv')
{
$fp = fopen($filename, 'w');
// CSV头部
fputcsv($fp, ['Server', 'Status', 'Output', 'Error']);
foreach ($this->results as $result) {
fputcsv($fp, [
$result['server'],
$result['status'],
$result['output'] ?? '',
$result['error'] ?? ''
]);
}
fclose($fp);
return $filename;
}
}
结果汇总与展示
<?php
class ResultAggregator
{
public static function aggregate($results)
{
$aggregated = [
'total_commands' => 0,
'successful' => 0,
'failed' => 0,
'errors' => [],
'servers_summary' => []
];
foreach ($results as $serverResult) {
$serverKey = $serverResult['server'];
if (!isset($aggregated['servers_summary'][$serverKey])) {
$aggregated['servers_summary'][$serverKey] = [
'total' => 0,
'success' => 0,
'fail' => 0
];
}
if ($serverResult['status'] === 'success') {
$aggregated['successful']++;
$aggregated['servers_summary'][$serverKey]['success']++;
} else {
$aggregated['failed']++;
$aggregated['servers_summary'][$serverKey]['fail']++;
$aggregated['errors'][] = $serverResult['error'];
}
$aggregated['total_commands']++;
$aggregated['servers_summary'][$serverKey]['total']++;
}
return $aggregated;
}
public static function renderHTML($results)
{
$html = '<table border="1">';
$html .= '<tr><th>Server</th><th>Command</th><th>Status</th><th>Output</th></tr>';
foreach ($results as $result) {
$statusClass = $result['status'] === 'success' ? 'success' : 'error';
$html .= sprintf(
'<tr class="%s"><td>%s</td><td>%s</td><td>%s</td><td><pre>%s</pre></td></tr>',
$statusClass,
htmlspecialchars($result['server']),
htmlspecialchars($result['command'] ?? 'N/A'),
htmlspecialchars($result['status']),
htmlspecialchars($result['output'] ?? $result['error'] ?? '')
);
}
$html .= '</table>';
return $html;
}
}
推荐使用场景
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| phpseclib | 无SSH2扩展的环境 | 纯PHP实现,兼容性好 | 性能相对较慢 |
| SSH2扩展 | 高性能需求 | 速度快,原生支持 | 需要安装扩展 |
| 并行执行 | 大规模服务器集群 | 效率高 | 资源消耗大 |
| Symfony Process | 生产环境 | 功能完善,异常处理好 | 依赖包较重 |
建议
- 生产环境:建议使用
phpseclib或Symfony Process组件 - 性能优化:对大量服务器使用并行执行
- 错误处理:一定要捕获并记录SSH连接失败、命令执行失败等异常
- 安全考虑:使用密钥认证代替密码,或使用SSH配置管理工具
- 结果持久化:可以将结果保存到数据库或文件系统中
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播