无法通过php exec运行shell命令,但可以在shell用户下运行?

5
我正在尝试在我的专用服务器上使用exiftool。问题是,PHP exec似乎与以用户身份运行命令时不同。奇怪的是,PHP显示为与我登录的相同用户,但它在系统命令方面的行为不同。
奇怪的是,一切都在我的本地主机上运行良好,但在服务器上却无法正常工作。
因此,就像提到的那样,通过ssh登录运行exiftool命令是可以的。
但是,在一个php测试脚本中运行(请注意,我已经在每个测试目录中安装了exiftool,并且通过ssh运行),虽然它以orangeman用户身份运行,但没有任何东西可以访问。
而且它失败了。
这里有一个更新 - 一整天都在这上面:
在shell上:
-bash-4.1$ which exiftool -a
~/perl5/bin/exiftool
/usr/bin/exiftool
~/perl5/bin/exiftool

在PHP中使用shell_exec('exiftool -a');
/usr/bin/exiftool

以下是该文件链接的内容:

lrwxrwxrwx    1 root root          33 May 15 02:10 exiftool -> /home/orangeman/perl5/bin/exiftool

我还尝试过创建各种符号链接,通过在php中使用putenv();篡改主要的$PATH变量...但我真的一无所知。在本地主机上可以工作,在专用服务器上不行。
我已经加了赏金 - 这是一个严重的开发问题。
我在专用服务器上,问题如上所述。
更新 根据@gcb的建议,我能够打印出错误,当php的exec()函数运行系统命令时没有效果。
PHP
<?php
exec('exiftool 2>&1', $output, $r);
var_dump($output, $r);
?>

输出:

array(2) {
  [0]=>
  string(230) "Can't locate Image/ExifTool.pm in @INC (@INC contains: /bin/lib /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at /bin/exiftool line 33."
  [1]=>
  string(59) "BEGIN failed--compilation aborted at /bin/exiftool line 33."
}

更新

@gcb的解决方案已经生效。非常感谢。


PHP是否在chroot中?它是否有权限实际查看这些目录? - Zoredache
"which php" 返回 "/usr/local/bin/php"。 我不熟悉 'chroot',但如果可能的话,有什么解决方法? - Orangeman555
你需要将所需的文件/程序放在chroot中。至于它是什么,请参阅维基百科条目。http://en.wikipedia.org/wiki/Chroot - Zoredache
/usr/bin/exiftool 不是一个目录。 /usr/bin 是目录,而 /usr/bin/exiftool(如果存在)将是一个文件。 - that other guy
4个回答

5

现在你遇到的不是php问题,而是perl问题。你的包含路径有误。

答案在这里

You either have to install the ExifTool libraries in the
standard location (ie. somewhere in the @INC directories
listed in your post), or add the location to the include path,
something like this:

Code:
#!/usr/bin/perl
BEGIN { unshift @INC, "PATH_TO_DIRECTORY_CONTAINING_LIBRARIES" }
use Image::ExifTool;

You should be able to add "Image/ExifTool.pm" to the path you add
to find the ExifTool module.

- Phil

我仍然认为使用我在之前回答中提出的建议#3可以解决它。如果不行,而你真的想知道原因,创建一个新的perl脚本,只输出@INC的内容,并通过shell和php运行它。你会看到区别,然后需要找到哪个登录脚本在php中没有被执行,并对php的shell_exec不予尊重进行错误报告...
虽然你的问题的更容易的解决方案(因为看起来你对解释不是太感兴趣)是在调用脚本之前设置PERLLIB变量。
所以,只需执行以下操作:
1. find / -name ExifTool.pm这将告诉你库的安装位置。假设这返回/example/perl/Image/ExifTool.pm 2. 在你的exec()调用中添加PERL5LIB=/example/perl/。 3. exec("PERL5LIB=/example/perl/ /var/www/myscript/execscript.sh {$param}"); #and

“...因为看起来你似乎对解释不太感兴趣”...并非如此!我非常感兴趣。我已经在这上面花了将近一周的时间。这似乎是最有可能的答案。我正在检查它。我将其解压缩到临时目录并运行了“make”...假设它会正确安装。显然它没有按照标准方式安装。根据您的观察,我今天早上一直在研究这个问题。我对perl毫不了解,所以进展比较慢。 - Orangeman555
@Orangeman555 谢谢。关于学习,我真的不认为你需要学习 Perl 来解决这个问题。如果你想提高自己,学习 Linux(文件系统结构、文件权限、标准输出 vs 标准错误输出,即缓冲区和流,以及一个 shell 或另一个 shell)并了解你选择的工作环境(PHP、Apache,我假设),学会在哪里搜索错误日志等。最终你会发现一切都遵循相同的模式,变得容易 :) 干杯。 - gcb

2

0) 你应该查看错误日志,通常在 /var/log/apache/error 目录下。

它会有诸如 "access denied" 或其他消息。

1) 显然你没有得到足够的输出来看到任何错误。所以尝试用 exiftool 2>&1 运行它。这将重定向 stderr 到 stdout,因此错误将出现在输出中。不确定是否相关或者php已经这样做了。您可能还想使用 passthru 而不是 exec

2) 安全模式执行目录

您的文件可能超出了安全模式执行目录。请阅读以下内容:

http://www.php.net/manual/en/ini.sect.safe-mode.php#ini.safe-mode-exec-dir

3) 其他方法失败时,将命令作为登录 shell 运行,这应该与通过 ssh 登录时加载的脚本相同。只需将 exec 替换为 shell_exec

... 我相信查看错误日志将解决您的问题。


我现在正在查看您的建议。会尽快给您回复,非常感谢您。 - Orangeman555
太棒了的建议。我已经更新了原帖并附上了结果。我不知道如何从shell中获取错误日志,所以这是巨大的进步。现在我们看到它抛出了一个错误,显然当我们通过shell登录时没有遇到过,那么这是什么意思呢? - Orangeman555

1

一种可能性是在命令行上,您的$PATH由于命令行是一个登录 shell,因此已经被设置/修改为$HOME/.bashrc$HOME/.bash_profile两者影响。当Web服务器调用PHP时,它作为"orangeman"运行只作为shell而不是登录shell,因此其$PATH可能不同,这就是您所看到的问题。 您是否尝试将export PATH="what:you:want:your:PHP:path:to:be"放入$HOME/.bashrc中?


我曾经尝试过这个 - 我不确定我做得对不对。这应该在 PHP 脚本运行时完成,还是有一种永久设置系统的方法? - Orangeman555

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接