PHP未切换当前目录时exec执行ghostscript命令失效问题
先澄清一个误区:
exec()中执行的所有命令都运行在PHP拉起的独立子shell进程里,你在命令里写的cd只会修改这个子shell的工作目录,命令执行结束后子shell就销毁了,完全不会改动PHP主进程的当前工作目录,和你不想用chdir()修改PHP进程目录的需求没有冲突。
问题根因
- 你第一段可运行的代码里,
chdir('/user/bin/ghostscript')之后执行的是不带./前缀的gs命令,系统不会在当前切换的目录下找可执行文件,而是会从环境变量PATH定义的全局路径里搜索Ghostscript程序。也就是说你之前的chdir根本不是用来定位gs二进制的,只是刚好把工作目录切到了gs运行时默认读取配置/依赖的路径,才让命令跑通了。 - 第二段失效的代码里,cd到目录后执行的是
./gs,是强制调用/user/bin/ghostscript目录下的本地二进制文件,这个文件要么没有可执行权限、要么和主机系统架构不匹配、要么被主机的open_basedir安全限制拦截,才会执行失败。另外部分共享主机的PHP默认调用的受限shell不支持&&命令拼接符,也会导致整条命令解析失败。
修复方案
优先选第一种方案,稳定性最高:
- 先找到全局可正常调用的gs路径
把你第一段可运行的代码临时加一行调试,拿到全局gs的真实绝对路径:
一般输出的路径会是<?php chdir('/user/bin/ghostscript'); exec('which gs', $pathInfo); print_r($pathInfo);/usr/bin/gs或者/usr/local/bin/gs这类系统默认路径。 - 直接用绝对路径调用gs,不需要切任何目录
拿到路径后直接写全路径执行即可,既不需要调用PHP的chdir(),也不需要在命令里拼cd,完全不会修改PHP进程工作目录:
执行后如果<?php $a = '/home1/user/public_html/a.pdf'; $b = '/home1/user/public_html/b.pdf'; $gsBin = '/usr/bin/gs'; // 替换成你上一步打印出来的实际路径 // 路径参数用escapeshellarg转义,避免特殊字符导致命令失败 exec($gsBin . ' -sDEVICE=pdfwrite -dNOPAUSE -dBATCH -dSAFER -dFirstPage=1 -dLastPage=2 -sOutputFile=' . escapeshellarg($b) . ' ' . escapeshellarg($a), $cmdOutput, $returnCode);$returnCode为0说明运行成功,非0可以打印$cmdOutput看具体报错信息。
如果你必须使用/user/bin/ghostscript目录下的特定版本gs,用下面的写法适配共享主机的受限shell:
<?php $a = '/home1/user/public_html/a.pdf'; $b = '/home1/user/public_html/b.pdf'; // 用分号代替&&做命令分隔,兼容不支持&&的受限shell,同时给gs写全绝对路径避免解析错误 exec('cd /user/bin/ghostscript; /user/bin/ghostscript/gs -sDEVICE=pdfwrite -dNOPAUSE -dBATCH -dSAFER -dFirstPage=1 -dLastPage=2 -sOutputFile=' . escapeshellarg($b) . ' ' . escapeshellarg($a), $cmdOutput, $returnCode);
如果执行后返回码是126,说明该目录下的gs文件没有可执行权限,要么联系主机商开通权限,要么换用全局安装的gs版本即可。
内容的提问来源于stack exchange,提问作者TWolfe
相关产品推荐
相关产品推荐

