You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP exec()执行zip备份命令与终端结果不一致的问题排查

问题背景

有一个名为backup-generic的PHP进程,用于构建并执行zip命令备份目录。生成的命令如下:

zip --symlinks --recurse-paths --quiet /usr/local/backups/home_dennis-daily-2024-09-19.zip '/home/dennis' -x '.DS_Store' -x '._*' -x '/home/delovelady/.vscode-server/bin/*/node/*' -x '/home/delovelady/.vscode-server/data/*' 2>&1

该命令通过PHP的exec()执行时,压缩包仍包含本应通过-x排除的vscode文件;但将日志中的命令原样在ksh终端执行时,能正确排除文件,压缩包体积小300MB。相关执行代码:

$lov->writeLogLine( [ 'Executing command: %s', $backupCmd ] ) ;
$rc = 0 ;
$OK = exec($backupCmd, $output, $rc) ;

排除列表通过globFileList()函数处理,用escapeshellarg()对路径转义加单引号,拼接命令的逻辑为:

foreach ($exclusions as $exclude)
    $backupCmd .= ' -x' . $exclude ;

环境:Ubuntu 22.04.5 LTS,ksh 93u+m/1.0.0-beta.2,Zip 3.0。

原因分析
  1. 命令拼接的空格缺失
    代码中拼接排除项时,-x选项与后续的排除路径之间没有添加空格,导致最终命令中出现-x'/home/...'的格式。zip命令无法识别这种连写格式——它期望-x作为独立选项,后续路径作为单独参数,因此忽略了排除规则。而终端执行的命令带有空格(-x '/home/...'),zip能正确解析。

  2. Shell环境差异
    PHP的exec()默认使用系统的/bin/sh(Ubuntu 22.04中为dash),而终端使用ksh。dash与ksh对这种连写选项的解析行为不同:ksh可能会将-x'/path'拆分为-x和/path两个参数,而dash会将其作为单个参数传递给zip,进一步导致排除规则失效。

解决方法
  1. 修正命令拼接逻辑
    在-x和排除路径之间添加空格,确保选项与参数分离:

    foreach ($exclusions as $exclude)
        $backupCmd .= ' -x ' . $exclude ; // -x后添加空格
    
  2. 统一Shell环境(可选)
    强制PHP使用ksh执行命令,确保与终端环境一致:

    // 将原命令用ksh执行
    $execCmd = 'ksh -c ' . escapeshellarg($backupCmd);
    $rc = 0 ;
    $OK = exec($execCmd, $output, $rc) ;
    

内容的提问来源于stack exchange,提问作者Dennis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 04:47:11