PHP Conference Japan 2024

程序执行函数

注释

警告

在后台执行程序之前,应该关闭带有锁定的打开文件(特别是打开的会话)。

参见

这些函数也与反引号运算符密切相关。

目录

  • escapeshellarg — 转义用作 shell 参数的字符串
  • escapeshellcmd — 转义 shell 元字符
  • exec — 执行外部程序
  • passthru — 执行外部程序并显示原始输出
  • proc_close — 关闭由 proc_open 打开的进程并返回该进程的退出代码
  • proc_get_status — 获取由 proc_open 打开的进程的信息
  • proc_nice — 更改当前进程的优先级
  • proc_open — 执行命令并打开用于输入/输出的文件指针
  • proc_terminate — 终止由 proc_open 打开的进程
  • shell_exec — 通过 shell 执行命令并将完整的输出作为字符串返回
  • system — 执行外部程序并显示输出
添加注释

用户贡献的注释 33 条注释

valqk at lozenetz dot org
19 年前
如果您将 php chroot 到没有 /bin/sh 的环境中,则无法执行任何命令!即使您调用
mail() 并调用 sendmail ... 实际上 sendmail 从未被调用,因为 /bin/sh 不在 chroot 中!
所以总之:您必须拥有 /bin/sh 才能执行某些操作!!!
非常重要!
我在找到这个的过程中浪费了几天时间,希望它能帮助到其他人!!!
sam at freepeers dot com
20 年前
为了更清楚地说明前几篇文章中所说的内容

"exec"、"反引号"、"system" 等在 Windows 2003 中默认情况下将失败。

您必须修改 cmd.exe 的安全设置,以便为用户帐户 IUSR-计算机名授予必要的权限,这些权限至少为读取和执行。
onkelfilemon at gmail dot com
4 年前
请注意,`popen` 也是一个程序执行函数,但出于某种原因,此处未列出。
jpgiot at ifrance.com
21 年前
适用于 Windows 用户

我查看了启动命令。它实际上非常有用。

这是我启动特定 Apache SSL 服务器的方式

<?php
$cmd
= "start /D C:\OpenSA\Apache /B Apache.exe -D SSL";
exec($cmd,$output,$rv);
?>

/D 指定应用程序将启动的路径(相当于 cd 到路径)
/B 在没有控制台窗口的情况下启动应用程序

因此,要了解 start 的确切参数,您可以使用

<?php
exec
("start /? > start.txt");
?>

您将在运行脚本的同一位置的“start.txt”文件中获得 start 的帮助

对于 WIN 98,您可能需要稍微修改一下命令

exec("COMMAND.COM /C START program args >NUL");

(取自另一篇文章)
not at any dot com
18 年前
终于!坚持不懈得到了回报,几天来尝试了各种方法!

我不想使用批处理文件。我试图使用 PHP 来摆脱批处理文件。

我不想调用一个文件来解析参数以调用一个 shell 来调用一个文件。我现在有“系统”带有三层和五层深度的批处理。

我只是想运行一些东西。

CALL,在 WinXP 上测试,将立即在更多操作系统上测试,在 PHP4 和 5 中,使用 exec、system,适用于 popen 和 passthru。

这是我拙劣的示例函数。

// 创建 Zip
function createzip ($target, $archive)
{ $ziputil = "call \"c:\\Program Files\\7-zip\\7z.exe\"";
$archived = escapeshellarg($archive);
$targeted = escapeshellarg($target);

$shellcommand= $ziputil." a -tzip ".$archived." ".$targeted."\n";

// 以下所有方法在 Win XP Pro 中均有效
passthru ($shellcommand);
exec ($shellcommand);
system ($shellcommand);
shell_exec ($shellcommand);
$proc= popen ($shellcommand, "r"); //$proc 包含输出

Windows 上带有空格的长路径名!

所有内容都包含在一个长长的连接的命令行中,其中包含多个带引号的文件名参数

shell 脚本的快乐!
leaetherstrip at inbox dot ru
20 年前
关于 XP 用户的注释:XP-Home 版本不允许直接在文件和文件夹上设置权限。您应该使用 cacls 命令行实用程序来执行此操作。

例如

cacls c:\windows\system32\cmd.exe /E /G IUSR_ADMIN2003:F

为 IIS 用户提供对 cmd.exe 的完全访问权限(潜在的安全漏洞!),因此 PHP 可以派生并执行外部程序。
dotpointer
20 年前
Win32 / PHP 4.3.6...

如果您计划在服务器上启动显示消息框(需要服务器端确认的操作)或保留(例如 notepad.exe)的程序,并且 exec 命令似乎进入死循环,那么可能是您的程序已启动,但您看不到它。因为 Web 服务器作为系统进程运行,并且不允许它与桌面交互。

为了解决部分问题(查看您执行的程序),在 Windows 的控制面板中,转到管理->服务,右键单击服务器服务,转到属性,然后在第二个选项卡(登录?)上选中允许服务与桌面交互的复选框。单击确定。重新启动 Web 服务器,并确保它确实已重新启动(即查看任务管理器,确保服务已_关闭_),否则桌面选项将不起作用。

下一阶段将是阻止 PHP 等待进程完成(这就是导致 PHP“循环”的原因)。我通过创建一个小的 Delphi 应用程序解决了这个问题,该应用程序获取我要执行的文件的路径并通过 WinExec API 执行它,该 API 只启动子程序而不等待它完成 = 程序完成并且 PHP.exe 完成脚本。问题解决!

Delphi 代码片段
WinExec(PChar(<CMD>),SW_SHOW); // 将 <CMD> 替换为程序路径。
匿名
16 年前
我在此页面上找到了以下评论

[引用开始]
尝试使用以下代码严重失败,并出现各种结果:例如“无法派生”、“访问被拒绝”、“结果为空”,具体取决于我使用的设置……即使相同的代码在服务器本身上从命令行运行。

$retstr = exec('nslookup -type=mx myhost.com', $retarr);

我认为这将适用于 \system32\ 目录中的大多数程序,而不是 nslookup。

我必须学习以下内容最终有效
$retstr = exec('c:\php\safedir\nslookup -type=mx myhost.com', $retarr);

... 但仅在列出的前提条件下
1:nslookup.exe 放在(复制到)\php\safedir\ 目录中
2:\php\safedir\ 目录包含在系统 PATH 环境变量中
3:cmd.exe 文件放在 \php\ 中,如上面其他注释中所述


4:php.ini 设置中已设置目录“c:\php\safedir\”。
safe_mode_exec_dir = "c:\php\safedir\"
…根据您的系统设置,也可能在 php-activescript.ini 中设置。
5:nslookup 通过完整路径引用,否则将调用 \windows\system32\ 中的文件。由于权限不足,我遇到了空结果的情况!

希望这对某些人节省时间和避免头痛有所帮助。
【引文结束】

这太复杂了。只需要两件事
1. 为 IUSR 帐户授予对 C:\Windows\System32 目录下 cmd.exe 文件的读取和执行权限。
2. 为 IUSR 帐户授予对所需命令的读取和执行权限(例如:C:\Widnows\System23 目录下的 nslookup.exe)。

仅需这两个条件,exec 就能正常工作。

(适用于在 Windows 平台上运行的 IIS 服务器)
[email protected]
16 年前
你好

我在执行 exec()、shell_exec() 时遇到了问题(PHP 未在安全模式下运行)。

经过一段时间的研究,我发现问题出在系统启用了 SELinux。

您可以查看日志 /var/log/messages 来确认这一点。
所以我禁用了 SELinux,现在一切正常了。

临时禁用 selinux:#> setenforce 0;

由于我认为这不是一个好主意,我仍然浏览了一些网站并找到了一个我认为不错的解决方案。

Fedora 的 SELinux 策略有一些布尔值 - 让我们看看它对我们的 apache 有什么设置。
# /usr/sbin/getsebool -a | grep httpd
allow_httpd_anon_write -- off
allow_httpd_mod_auth_pam -- off
allow_httpd_sys_script_anon_write -- off
httpd_builtin_scripting -- on
httpd_can_network_connect -- off
httpd_can_network_connect_db -- off
httpd_can_network_relay -- off
httpd_disable_trans -- off
httpd_enable_cgi -- on
httpd_enable_ftp_server -- off
httpd_enable_homedirs -- on
httpd_rotatelogs_disable_trans -- off
httpd_ssi_exec --off
httpd_suexec_disable_trans -- off
httpd_tty_comm -- off
httpd_unified -- on

从上面我们可以确定 exec() 不工作是因为这个 httpd_ssi_exec -- off 设置。
所以我把它打开。

#> setsebool -P httpd_ssi_exec 1;

现在我可以看到 shell_exec('ls -al') 的输出了。

Moses
[email protected]
16 年前
致 Lancelot

很简单,在 Windows 上 exec($my_cmd) 会执行 'cmd /c $my_cmd'
如果 $my_cmd 中有任何空格,则必须将其放在双引号中。
cmd /c "$app $target" 将起作用。

此外,如果 $app 和 $target 中有任何空格,它们也必须放在双引号中。
cmd /c ""$app" "$target""

因此,将您的 $cmd 包含在两个额外的双引号中,并且不要忘记转义反斜杠 \\。

$cmd = "\"\"C:\\my path with spaces\\targetapp.exe\" \"C:\\mypathnospaces\\targetfile.xxx\"\"";

然后 exec($cmd) 将起作用 :)
不再需要 chdir 了…
[email protected]
16 年前
要派生一个进程,我使用以下代码

<?php
fclose
(STDOUT); //关闭所有输出,否则将不起作用
fclose(STDIN);
fclose(STDERR);

if(
pcntl_fork()) {
exit;
//返回调用方
}

//在后台运行的代码

pcntl_exec("/usr/bin/php",Array($_SERVER['argv'][1]));

?>

我从我的网站上使用以下代码调用此脚本
<?php
function fork($script){ //运行相对于文档根目录的分支 PHP 脚本。不显示任何输出!
global $config;
$cmd = "/usr/bin/php \"" . dirname($_SERVER['SCRIPT_FILENAME']) . "/includes/forked/forkHelper.php\" \"" . $script . "\"";
exec($cmd);
}
?>
[email protected]
17 年前
如果您尝试使用 exec 运行 cgi 并通过 php 文件输出内容,则需要将标头与内容分开并分别输出。

此外,至少在我的尝试执行的 cgi 的情况下,缺少换行符,导致某些 javascript 无法正常工作,因此您可能需要将换行符添加回结果输出中。这是我用来正确输出 cgi 的代码…

<?PHP
putenv
('REQUEST_METHOD=GET');

//获取传递给 cgi 的参数..
putenv('QUERY_STRING=' . $_SERVER['QUERY_STRING']);

//当您遇到空行('')时,您将知道您已越过标头
//hit a blank line ('')
$inheader=true;
foreach(
$output as $val){
if(
$val=='')
$inheader=false;
if(
$inheader)
header($val);
else
echo(
$val . "\n"); //输出内容
}
?>
[email protected]
17 年前
使用 schtasks.exe 安排 WinXP 任务并使用 PHP 执行命令有时可能会失败。

这是因为,Apache 在放置调度时没有权限访问某些系统文件。我的做法是:创建一个普通用户帐户,并将 Apache 服务分配给以该帐户登录。

在“运行”窗口中打开“services.msc”,在列表中查找 Apache,右键单击并转到“属性”。单击第二个选项卡“登录”,然后填写“此帐户”字段。

当然,Apache 需要在首次安装时作为服务安装。

希望这对任何人都有所帮助。

Fendy Ahmad
匿名
17 年前
在我看来,最好的方法是将 cmd.exe 复制到 Web 目录下的某个安全目录中,将其添加到 Windows 路径中,然后从那里使用系统命令调用它。这似乎可以规避任何安全漏洞。
Matt-kun
17 年前
只是一个简单的提示,帮助人们解决一些头痛问题。

使用
<?
$filepath = "文件的路径";
exec("program -command $filepath ");
fopen($filepath,rw);
?>

确保您对 Linux/unix 路径使用“\ ”(\ 空格),对 fopen 使用“ ”空格。这些都是非常基本的事情,您通常不会认为会导致问题,但是当您尝试将斜杠传递给 fopen 时,它要么中断,要么更糟糕的是工作不正确 =D。反之亦然,对于任何使用“\ ”表示文件路径/名称中空格的程序。

我在使用 <? exec(" cp $path "); ?> 和 <? fopen("$path"); ?> 时遇到了此问题。为了解决它,我使用了 str_replace(" ","\ ",$path); 和 str_replace("\ ", " ",$path);

*注意,这有很多伪代码,并且有更快、更有效的方法来执行相同的操作,但我认为这可能会帮助那些对文件路径感到疯狂的人 =D。
[email protected]
18 年前
这些函数通常被认为是有害的,如果没有适当的输入验证过程,它们和 eval() 一样糟糕。

如果您尝试执行的操作可以使用其他机制完成,** 请使用该方法,即使它需要更多时间 **。

如果您的服务器由于代码中缺少 escapeshell*** 而被“入侵”,请不要哭泣或责怪 PHP,您已被警告。
[email protected]
19 年前
关于“无法派生”错误的更多信息。

它让我抓狂!我确实检查了 IUSR_[server] 帐户是否已设置读取和执行 cmd.exe 的权限。但它仍然失败了。直到我绝望了,才偶然发现了“特殊权限”。(单击常规权限列表下方的“高级”选项卡。)在那里我发现 IUSR_[server] 帐户不知何故拥有一个拒绝访问的特殊权限!当然,拒绝会覆盖其他所有内容。

这是新安装的 XP pro。它一定是某些内容(我猜是 IIS)的默认设置,以便设置此拒绝权限,只是为了让我们痛苦不堪。
[email protected]
19 年前
我在 FreeBSD 服务器(PHP 4.4.0)上使用 exec() 和 system() 命令将 shell 脚本作为后台进程时遇到了问题。

我尝试了上面提到的所有方法,但都没有成功,然后我编写了这个脚本,并且确定它可以在所有 Linux 版本上运行…看看它是否对你们所有人都有帮助…

我把我的 Shell 脚本放在变量 $ExecCommand 中。

并使用了 proc_close 和 proc_open 函数,代码如下:

proc_close (proc_open ($ExecCommand,array(),$somefun));

在上面这行代码之后,其余的普通 php 脚本继续执行…

希望它对你们有所帮助。
[email protected]
19 年前
嗯,当我想从 PHP 启动一个无限运行的 PERL 脚本时,遇到了这个问题。不知何故,我无法让 PHP 脚本在执行 PERL 脚本后返回。所以最终我找到了以下解决方案

PHP 文件
-------------------
<?php
exec
("perl /home/chatserver.pl > /dev/null");
?>
-------------------
此脚本将通过 HTTP 运行。

PERL 文件
-------------------
#!/usr/bin/perl
fork 并退出 1;

# 这里是一般的代码...
-------------------

希望它能帮助到某些人 :)
Margus
sean @T keenio - dot - com
19 年前
澄清一下,后台 shell exec() 与 Unix 下的 fork() 不同。如果 PHP 像 Perl 和其他脚本语言一样支持 fork() 则会很有用。简单地 exec() 一个进程并将其发送到后台并不相同。

真正的 fork() 会复制当前环境并复制正在运行的脚本,以便有两个实例,一个子进程,一个父进程。子进程和父进程可以确定它们是哪个副本,然后根据该副本采取不同的执行路径。通常,一个副本会执行一些“繁忙工作”,而另一个副本则返回到正常的执行。但无论如何,这两个函数都存在于同一代码中,而不是一个单独的外部程序。exec() 无法为您做到这一点。
vosechu at roman-fleuve dot com
20 年前
一个快速图表,对我帮助很大(每个命令的最佳 (+) / 最差 (o)),以及何时使用每个命令 (w)。

| exec(string cmd, [array cmd_output, [int cmd_exit_status]])
+- 第二个(可选)参数保存一个包含输出的数组,一行一行。
+- 第三个(可选)参数保存已执行程序的退出状态。
w- 当命令的输出仅对程序重要时。

| system(string cmd, [int cmd_exit_status])
+- 将 ASCII 输出直接回显到浏览器(仅 stdout;stderr 必须通过在命令后添加 2>&1 来重定向,以查看错误)。
+- 第二个(可选)参数保存已执行程序的退出状态。
w- 当命令的输出对用户很重要时。

我越看我的笔记,就越不关心最后两个函数。
Shell_exec() = exec() + implode() - 退出代码。
虽然需要能够通过 php 传递二进制文件,但大多数人永远不会使用 passthru

| passthru(string cmd, [int cmd_exit_status])
+- 将二进制输出直接回显到浏览器窗口。
+- 第二个(可选)参数保存已执行程序的退出状态。
w- 当命令的输出为二进制时。

| shell_exec(string cmd) <-- 最小功能以保持与正常使用反引号的同步。
+- 将完整输出作为字符串返回,而不是直接返回到浏览器(仅 stdout;stderr 必须重定向)。
o- 无法检查退出状态(应使用 system() 将 stderr 和 stdout 都重定向到 /dev/null 以获取退出代码)。
ferchland at computer-kontor dot de
20 年前
我尝试在 WinNT、Apache 2、PHP4 上派生一个 GUI 进程。
我在任务管理器进程列表中看到了新进程(notepad.exe),但任何地方都没有记事本窗口!
在服务 -> Apache 中激活“允许与桌面交互”复选框后,一切都可以使用简单的命令工作
exec("start c:\\winnt\\notepad.exe");
dens at dodgeball dot com
21 年前
昨晚我花了大约一个半小时的时间试图让 Perl 在运行 exec() 命令后将值传回我的 PHP 脚本。

由于我的 Perl 脚本正在使用 STDOUT(在传递命令行变量后),我预计在我的 .pl 脚本中使用的变量可以在 PHP 中访问,无需任何疑问。

当然,这不起作用,我最终在朋友的帮助下(Shawn = 超级明星)解决了这个问题:您需要回显 exec() 命令才能将值返回到 PHP 中。

由于我返回了多个值,因此我在我的 .pl 脚本中添加了管道分隔符,然后使用 PHP 解析字符串以将我的数据检索到不同的变量中。

看看

# 使用数据访问 .pl 脚本
$mydata = exec("script.pl 'command line args' ");
exec("exit(0)");

# 解析从 .pl 脚本返回的值
list($strA, $strB) = split ('[|]', $mydata);
echo "valueA: " . $strA."<br>";
echo "valueB: " . $strB."<br>";

耶!
/d
software at yvanrodrigues dot com
21 年前
关于 Windows 中无法派生的消息的更多信息

我想确认此问题与 %SYSTEMROOT%\SYSTEM32 上的权限有关。用户(通常是匿名登录)必须对 cmd.exe 具有执行权限

这与大多数其他编程语言不同。例如,在 C 中,spawn 和 exec 函数不会尝试打开 shell,它们直接创建新进程。PHP 通过 cmd.exe(或 command.com)创建新进程,就像 C system() 函数一样。这对那些试图运行批处理文件的人来说很好,但对于运行其他 .exe 文件来说非常低效。

我对提升 system32 目录中的权限感到不安,但您可以通过将 cmd.exe 复制到您的 PHP 目录来解决此问题。Windows 将首先在那里查找,如果不在那里,它将检查路径。注意:我指的是 php.exe 所在的目录,而不是您的脚本目录。

我已经通过在尝试执行脚本时运行 filemon.exe 确认了这一点,您可以看到它试图启动 cmd.exe 进程。
qscripts at lycos dot nl
21 年前
为什么不使用 Windows 每个版本都提供的“start”实用程序在 Windows 计算机上后台启动进程?例如

exec("start song.mp3");

这将导致 .MP3 文件类型的默认处理应用程序开始播放选定的歌曲。
kop at meme dot com
22 年前
据我所知,标准的 Unix Apache 配置在后台运行作业时会导致一个罕见的问题。MaxRequestsPerChild 指令会导致子进程在 1000 个请求后终止,与子进程关联的任何后台进程都将与子进程一起死亡,除非它们是使用“nohup”命令启动的。因此,在后台启动作业的正确方法是使用

exec('nohup my-command > /dev/null 2>&1 &')
ramirosuarez at fibertel dot com dot ar
22 年前
适用于 Windows 用户

请记住,许多“无法派生”错误是由于权限不足造成的。

CMD.EXE、TEMP 目录(或您在 php.ini 中指定的任何目录)以及您用于上传或操作文件的目录都需要具有写入权限?通常是用户 USER。

这将对所有使用 GD 库或其他操纵图形图像的程序的人员很有用。
GCMXZPJPDJER at spammotel dot com
22 年前
要从 php 后台执行脚本,您不需要使用 mikehup 或其他工具。只需执行

`/usr/bin/php -q foobar.php >/dev/null 2>&1 &`;

mat
daniel dot hopp at godot dot de
22 年前
关于:session_write_close()

要在会话中启动后台进程,我建议
使用启动-停止守护进程并通过 ENV 传递参数。

示例
----------------------------------------
<?php
// 在会话中..
exec("/some/where/launch.sh 300");
// ..立即完成。
?>

----------------------------------------
#!/bin/bash
# /some/where/launch.sh
T=$1
export T
DAEMON=/some/where/runme.pl
start-stop-daemon --start --quiet --background --exec $DAEMON

----------------------------------------
#!/usr/bin/perl
# /some/where/runme.pl
my $t = $ENV{'T'};
sleep($t);

----------------------------------------
manokan at manokan dot net
18 年前
让我们更清楚地说明一下,关于 exec() 中的“无法派生”错误。

默认情况下,Windows XP 将临时 Internet 用户帐户 (IUSER-[computername]) 的 cmd.exe 的所有权限设置为拒绝。这会覆盖所有内容。

您必须修改 cmd.exe 的安全设置,至少为 IUSER-computername 帐户授予读取和执行权限,并删除拒绝权限。
Shaun
19 年前
尝试使用“exec”在 Win2K3(据说这也会在 Win2K 上发生)上运行辅助可执行文件,该可执行文件来自 IIS 上运行的 PHP 脚本,但未能调用可执行文件。

在使用 Apache 提供服务的 Win2K 上运行相同的 PHP 脚本,调用相同的辅助可执行文件,则可以正常工作。

解决方案:在 Win2K3(以及可能在 Win2K 上),为 IIS IUSR_xxxxx 来宾用户授予对位于 Windows 根目录下的 System32 文件夹中的 Cmd.exe 的读取和执行权限。

这个困惑了我比应该的时间更长!希望这能帮助其他人免受同样的命运!
kristof_jebens at hotmail dot com
21 年前
对于运行 Apache 1.3.22 的 WIN2K Server 用户,如果无法运行可执行文件...

exec('c:\\WINNT\\system32\\cmd.exe /c START c:\\file.exe');

这是唯一对我有用的方法。
希望对您有所帮助
deschampsbest at noos dot fr
22 年前
我需要知道何时一组使用 system 调用启动的后台任务已终止。
我找不到要调用的函数,所以我创建了一个小函数。

它基本上循环检查 ps 命令返回的结果中是否存在字符串。

我所有在后台运行的命令都包含 posix_getpid 返回的 pid。

我能够启动许多后台任务并有效地等待它们全部完成。

在此之前,我基本上会休眠 N 秒。

如果存在众所周知或更好的方法,我深感歉意。

谢谢,
新手
===============
===============

function wait_until_termination ($id, $duration){
$i = 0;
do {
$i += 1;

$cnt = `ps -auxww |
grep $id |
grep -v grep |
awk '{print $2}' |
grep -v $id|
wc -l` ;

if ($cnt > 0) {
sleep ($duration);
}

} while ($cnt != 0);

echo ("wait_until_termination (PID: $id, SEC: $duration) : LOOPS
:$i<BR>");
}
To Top