php 如何调试 exec() 问题?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12199353/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I debug exec() problems?
提问by carcargi
The exec command doesn't work on my server, it does not do anything, I've had safe_mode off, and verified that all the console commands are working, I've tried with absolute paths. I've checked the permissions on the applications and all the applications I need have execution permissions. I don't know what else to do, here are the rundown of the codes I've tried.
exec 命令在我的服务器上不起作用,它没有做任何事情,我已经关闭了 safe_mode,并验证了所有控制台命令都在工作,我已经尝试过使用绝对路径。我已经检查了应用程序的权限,我需要的所有应用程序都具有执行权限。我不知道还能做什么,这是我尝试过的代码的概要。
echo exec('/usr/bin/whoami');
echo exec('whoami');
exec('whoami 2>&1',$output,$return_val);
if($return_val !== 0) {
echo 'Error<br>';
print_r($output);
}
exec('/usr/bin/whoami 2>&1',$output,$return_val);
if($return_val !== 0) {
echo 'Error<br>';
print_r($output);
}
The last two codes display:
最后两个代码显示:
Error
Array ( )
I've contacted the server service and they can't help me, they don't know why the exec command isn't working.
我已经联系了服务器服务,他们帮不了我,他们不知道为什么 exec 命令不起作用。
采纳答案by Kuf
have a look at /etc/php.ini, there under:
看看/etc/php.ini,在下面:
; This directive allows you to disable certain functions for security reasons.
; It receives a comma-delimited list of function names. This directive is
; *NOT* affected by whether Safe Mode is turned On or Off.
; http://www.php.net/manual/en/ini.sect.safe-mode.php#ini.disable-functions
disable_functions =
make sure that exec is not listed like this:
确保 exec 没有像这样列出:
disable_functions=exec
If so, remove it and restart the apache.
如果是这样,请将其删除并重新启动 apache。
For easy debugging I usually like to execute the php file manually (Can request more errors without setting it in the main ini). to do so add the header:
为了方便调试,我通常喜欢手动执行 php 文件(可以请求更多错误而不在主 ini 中设置)。为此添加标题:
#!/usr/bin/php
ini_set("display_errors", 1);
ini_set("track_errors", 1);
ini_set("html_errors", 1);
error_reporting(E_ALL);
to the beginning of the file, give it permissions using chmod +x myscript.phpand execute it ./myscript.php. It's very heedful especially on a busy server that write a lot to the log file.
到文件的开头,给它使用权限chmod +x myscript.php并执行它./myscript.php。这是非常谨慎的,尤其是在繁忙的服务器上,会向日志文件写入大量内容。
EDIT
编辑
Sounds like a permissionsissue. Create a bash script that does something simple as echo "helo world"and try to run it. Make sure you have permissions for the file and for the folder containing the file. you chould just do chmod 755just for testing.
听起来像是权限问题。创建一个执行简单操作的 bash 脚本echo "helo world"并尝试运行它。确保您对该文件和包含该文件的文件夹具有权限。你可以只做chmod 755测试。
回答by Mike Mackintosh
Since you are dropping out of the PHP context into the native shell, you are going to have a lot of issues debugging.
由于您要从 PHP 上下文中退出到本机 shell,因此您将在调试时遇到很多问题。
The best and most foolproof I have used in the past is writing the output of the script to a log file and tailing it during PHP execution.
我过去使用过的最好和最简单的方法是将脚本的输出写入日志文件并在 PHP 执行期间对其进行跟踪。
<?php
shell_exec("filename > ~/debug.log 2>&1");
Then in a separate shell:
然后在一个单独的外壳中:
tail -200f ~/debug.log
When you execute your PHP script, your errors and output from your shell call will display in your debug.logfile.
当您执行 PHP 脚本时,shell 调用的错误和输出将显示在您的debug.log文件中。
回答by YohannP
回答by mario
A few more notes
还有一些注意事项
For debugging always wrap your exec/shell_exec function in
var_dump().error_reporting(-1);should be on, as should bedisplay_errors, as last resort evenset_error_handler("var_dump");- if only to see if PHP itself didn't invokeexecvpor else.Use
2>&1(merge the shells STDERR to STDOUT stream) to see why an invocation fails.
For some cases you may need to wrap your command in an additional shell invocation:// capture STDERR stream via standard shell shell_exec("/bin/sh -c 'ffmpeg -opts 2>&1' ");Else the log file redirectas advised by @Mike is the most recommendable approach.
Alternate between the various exec functions to uncover error messages otherwise. While they mostly do the same thing, the output return paths vary:
exec()→ either returns the output as function result, or through the optional$outputparamater.
Also provides a$return_varparameter, which contains the errno / exit code of the run application or shell. You might get:ENOENT(2) - No such fileEIO(127) - IO error: file not found
shell_exec()→ is what you want to run mostly for shell-style expressions.
Be sure to assign/print the return value with e.g.var_dump(shell_exec("..."));``inline backticks → are identical toshell_exec.system()→ is similar toexec, but always returns the output as function result (print it out!). Additionally allows to capture the result code.passthru()→ is anotherexecalternative, but always sends any STDOUT results to PHPs output buffer. Which oftentimes makes it the most fitting exec wrapper.popen()or betterproc_open()→ allow to individually capture STDOUT and STDERR.
Most shell errors wind up in PHPs or Apaches
error.logwhen not redirected. Check your syslog or Apache log if nothing yields useful error messages.
对于调试,始终将 exec/shell_exec 函数包装在
var_dump().error_reporting(-1);应该是,应该是display_errors,作为最后的手段set_error_handler("var_dump");- 如果只是为了看看 PHP 本身是否没有调用execvp,否则。使用
2>&1(将 shell STDERR 合并到 STDOUT 流)查看调用失败的原因。
在某些情况下,您可能需要将命令包装在额外的 shell 调用中:// capture STDERR stream via standard shell shell_exec("/bin/sh -c 'ffmpeg -opts 2>&1' ");否则@Mike 建议的日志文件重定向是最值得推荐的方法。
在各种 exec 函数之间交替以发现错误消息。虽然它们大多做同样的事情,但输出返回路径各不相同:
exec()→ 要么将输出作为函数结果返回,要么通过可选$output参数返回。
还提供了一个$return_var参数,其中包含运行应用程序或shell的errno/exit代码。你可能会得到:ENOENT(2) - 没有这样的文件EIO(127) - IO 错误:找不到文件
shell_exec()→ 是您主要希望为 shell 样式表达式运行的内容。
一定要分配/打印返回值,例如var_dump(shell_exec("..."));``内联反引号 → 与shell_exec.system()→ 类似于exec,但总是将输出作为函数结果返回(打印出来!)。另外允许捕获结果代码。passthru()→ 是另一种exec选择,但始终将任何 STDOUT 结果发送到 PHP 输出缓冲区。这通常使它成为最合适的 exec 包装器。popen()或者更好proc_open()→ 允许单独捕获 STDOUT 和 STDERR。
大多数 shell 错误
error.log在未重定向时最终会出现在 PHP 或 Apache 中。如果没有任何有用的错误消息,请检查您的 syslog 或 Apache 日志。
Most common issues
最常见的问题
As mentioned by @Kuf: for outdated webhosting plans, you could still find
safe_modeordisable_functionsenabled. None of the PHP exec functions will work. (Best to find a better provider, else investigate "CGI" - but do notinstall your own PHP interpreter while unversed.)Likewise can AppArmor / SELinux / Firejailsometimes be in place. Those limit each applications ability to spawn new processes.
The intended binary does not exist. Pretty much no webhost does have tools like
ffmpegpreinstalled. You can't just run arbitrary shell commands without preparation. Some things need to be installed!// Check if `ffmpeg` is actually there: var_dump(shell_exec("which ffmpeg"));The
PATHis off. If you installed custom tools, you will need to ensure they're reachable. Usingvar_dump(shell_exec("ffmpeg -opts"))will search all common paths - or as Apache has been told/constrained (often just/bin:/usr/bin).Check with
print_r($_SERVER);what your PATH contains and if that covers the tool you wanted to run. Else you may need to adapt the server settings (/etc/apache2/envvars), or use full paths:// run with absolute paths to binary var_dump(shell_exec("/bin/sh -c '/usr/local/bin/ffmpeg -opts 2>&1'"));This is somewhat subverting the shell concept. Personally I don't think this preferrable. It does make sense for security purposes though; moreover for utilizing a custom installation of course.
Permissions
In order to run a binary on BSD/Linux system, it needs to be made "executable". This is what
chmod a+x ffmpegdoes.Furthermode the path to such custom binaries needs to be readable by the Apache user, which your PHP scripts run under.
More contemporary setups use PHPs builtin FPM mode (suexec+FastCGI), where your webhosting account equals what PHP runs with.
Test with SSH. It should go without saying, but before running commands through PHP, testing it in a real shell would be highly sensible. Probe with e.g.
ldd ffmpegif all lib dependencies are there, and if it works otherwise.Input values (GET, POST, FILE names, user data) that get passed as command arguments in execstrings need to be escaped with
escapeshellarg().$q = "escapeshellarg"; var_dump(shell_exec("echo {$q($_GET['text'])} | wc"));Otherwise you'll get shell syntax errors easily; and probably exploit code installed later on...
Take care not to combine backticks with any of the
*exec()functions:$null = shell_exec(`wc file.txt`); ↑ ↑Backticks would run the command, and leave shell_exec with the output of the already ran command. Use normal quotes for wrapping the command parameter.
Also check in a shell session how the intended program works with a different account:
sudo -u www-data gpg -kNotably for PHP-FPM setups test with the according user id.
www-data/apacheare mostly just used by olden mod_php setups.Many cmdline tools depend on some per-user configuration. This test will often reveal what's missing.
正如@Kuf 所提到的:对于过时的虚拟主机计划,您仍然可以找到
safe_mode或disable_functions启用。PHP exec 函数都不起作用。(最好找一个更好的提供商,否则调查“CGI”——但不要在不了解的情况下安装自己的 PHP 解释器。)同样,AppArmor / SELinux / Firejail有时也可以。这些限制了每个应用程序产生新进程的能力。
预期的二进制文件不存在。几乎没有虚拟主机确实有像
ffmpeg预装这样的工具。你不能在没有准备的情况下运行任意的 shell 命令。有些东西需要安装!// Check if `ffmpeg` is actually there: var_dump(shell_exec("which ffmpeg"));该
PATH关闭。如果您安装了自定义工具,则需要确保它们可以访问。Usingvar_dump(shell_exec("ffmpeg -opts"))将搜索所有常见路径 - 或者正如 Apache 被告知/限制(通常只是/bin:/usr/bin)。检查
print_r($_SERVER);您的 PATH 包含什么以及是否涵盖了您想要运行的工具。否则,您可能需要调整服务器设置 (/etc/apache2/envvars),或使用完整路径:// run with absolute paths to binary var_dump(shell_exec("/bin/sh -c '/usr/local/bin/ffmpeg -opts 2>&1'"));这在某种程度上颠覆了shell的概念。我个人认为这不是可取的。不过,出于安全目的,它确实有意义;此外,当然还可以使用自定义安装。
权限
为了在 BSD/Linux 系统上运行二进制文件,它需要成为“可执行的”。这就是
chmod a+x ffmpeg它的作用。此外,此类自定义二进制文件的路径需要Apache 用户可读,您的 PHP 脚本在其下运行。
更现代的设置使用 PHP 内置 FPM 模式 (suexec+FastCGI),您的虚拟主机帐户等于 PHP 运行的帐户。
使用SSH 进行测试。不言而喻,但在通过 PHP 运行命令之前,在真正的 shell 中测试它是非常明智的。例如,
ldd ffmpeg如果所有 lib 依赖项都存在,以及它是否可以正常工作,则进行探测。在 exec字符串中作为命令参数传递的输入值(GET、POST、FILE 名称、用户数据)需要使用
escapeshellarg().$q = "escapeshellarg"; var_dump(shell_exec("echo {$q($_GET['text'])} | wc"));否则你很容易得到 shell 语法错误;并且可能利用稍后安装的代码......
注意不要将反引号与任何
*exec()函数结合使用:$null = shell_exec(`wc file.txt`); ↑ ↑反引号将运行该命令,并将 shell_exec 与已运行命令的输出一起保留。使用普通引号来包装命令参数。
还要在 shell 会话中检查预期的程序如何与不同的帐户一起工作:
sudo -u www-data gpg -k特别是对于使用相应用户 ID 的 PHP-FPM 设置测试。
www-data/apache大多只是由旧的 mod_php 设置使用。许多 cmdline 工具依赖于一些每个用户的配置。此测试通常会揭示缺少的内容。
On Windows
在 Windows 上
CMD invocations will not play nice with STDERR streams often.
Definitely try a Powershell script to run any CLI apps else, or use a command line like:
system("powershell -Command 'pandoc 2>&1'");Use full paths, and prefer forward slashes always (
"C:/Program Files/Whatevs/run.exe"with additional quotes if paths contain spaces).Forward slashes work on Windows too, ever since they were introduced in MS-DOS 2.0
Figure out which service and SAM accountIIS/Apache and PHP runs as. Verify it has execute permissions.
You can't run GUI apps usually. (Typical workaround is the taskscheduler or WMI invocations.)
CMD 调用不会经常与 STDERR 流配合使用。
一定要尝试使用 Powershell 脚本来运行其他任何 CLI 应用程序,或者使用如下命令行:
system("powershell -Command 'pandoc 2>&1'");使用完整路径,并且总是喜欢使用正斜杠(
"C:/Program Files/Whatevs/run.exe"如果路径包含空格,则使用额外的引号)。正斜杠也适用于 Windows,自从它们在 MS-DOS 2.0 中引入以来
找出运行 IIS/Apache 和 PHP 的服务和 SAM 帐户。验证它具有执行权限。
您通常无法运行 GUI 应用程序。(典型的解决方法是 taskcheduler 或 WMI 调用。)

