exec

(PHP 4, PHP 5, PHP 7, PHP 8)

exec执行外部程序

描述

exec(string $command, array &$output = null, int &$result_code = null): string|false

exec() 执行给定的 command

参数

command

将要执行的命令。

output

如果存在 output 参数,则指定数组将填充来自命令的每一行输出。 结尾空格,例如 \n,不包含在此数组中。 注意,如果数组已经包含一些元素,exec() 将追加到数组的末尾。 如果您不希望函数追加元素,请在将数组传递给 exec() 之前调用 unset()

result_code

如果 result_code 参数与 output 参数一起存在,则执行的命令的返回状态将写入此变量。

返回值

来自命令结果的最后一行。 如果您需要执行一个命令并直接将命令的所有数据传回,没有任何干扰,请使用 passthru() 函数。

如果失败,返回 false

要获取执行的命令的输出,请务必设置并使用 output 参数。

错误/异常

如果 exec() 无法执行 command,则发出 E_WARNING

如果 command 为空或包含空字节,则抛出 ValueError

变更日志

版本 描述
8.0.0 如果 command 为空或包含空字节,exec() 现在将抛出 ValueError。 以前它会发出 E_WARNING 并返回 false

示例

示例 #1 一个 exec() 示例

<?php
// 输出拥有运行 php/httpd 进程的用户名
// (在具有路径中的 "whoami" 可执行文件的系统上)
$output=null;
$retval=null;
exec('whoami', $output, $retval);
echo
"Returned with status $retval and output:\n";
print_r($output);
?>

上面的例子将输出类似于以下内容

Returned with status 0 and output:
Array
(
    [0] => cmb
)

注释

警告

当允许用户提供的数据传递给此函数时,请使用 escapeshellarg()escapeshellcmd() 来确保用户无法欺骗系统执行任意命令。

注意:

如果使用此函数启动程序,为了使其在后台继续运行,程序的输出必须重定向到文件或其他输出流。 否则,PHP 将挂起,直到程序执行结束。

注意:

在 Windows 上,exec() 将首先启动 cmd.exe 来启动命令。 如果您想在不启动 cmd.exe 的情况下启动外部程序,请使用 proc_open(),并将 bypass_shell 选项设置为 true。

参见

添加注释

用户贡献的注释 22 个注释

189
Arno van den Brink
15 年前
这将在 Windows 和 Unix 上在后台(没有 cmd 窗口)执行 $cmd,而不会让 PHP 等待它完成。

<?php
function execInBackground($cmd) {
if (
substr(php_uname(), 0, 7) == "Windows"){
pclose(popen("start /B ". $cmd, "r"));
}
else {
exec($cmd . " > /dev/null &");
}
}
?>
84
dell_petter at hotmail dot com
15 年前
(仅适用于 Linux 用户)。

我们现在知道如何在 Linux 中使用 & 运算符派生进程。
并使用命令:nohup MY_COMMAND > /dev/null 2>&1 & echo $!,我们可以返回进程的 PID。

这个小类是为了让您可以跟踪创建的进程(意味着启动/停止/状态)。

您可以使用它来启动一个进程或加入一个现有的 PID 进程。

<?php
// 您可以使用 status()、start() 和 stop()。 注意 start() 方法自动调用一次。
$process = new Process('ls -al');

// 或者,如果您获得了 PID,但只有 status() 方法将起作用。
$process = new Process();
$process.setPid(my_pid);
?>

<?php
// 然后您可以启动/停止/检查作业的状态。
$process.stop();
$process.start();
if (
$process.status()){
echo
"The process is currently running";
}else{
echo
"The process is not running.";
}
?>

<?php
/* 一种简单的跟踪外部进程的方法。
* 您是否曾经想在 PHP 中执行一个进程,但仍然希望对该进程有一定的控制?嗯.. 这是实现的一种方法。
* @兼容性: 仅限 Linux。(Windows 不起作用)。
* @作者: Peec
*/
class Process{
private
$pid;
private
$command;

public function
__construct($cl=false){
if (
$cl != false){
$this->command = $cl;
$this->runCom();
}
}
private function
runCom(){
$command = 'nohup '.$this->command.' > /dev/null 2>&1 & echo $!';
exec($command ,$op);
$this->pid = (int)$op[0];
}

public function
setPid($pid){
$this->pid = $pid;
}

public function
getPid(){
return
$this->pid;
}

public function
status(){
$command = 'ps -p '.$this->pid;
exec($command,$op);
if (!isset(
$op[1]))return false;
else return
true;
}

public function
start(){
if (
$this->command != '')$this->runCom();
else return
true;
}

public function
stop(){
$command = 'kill '.$this->pid;
exec($command);
if (
$this->status() == false)return true;
else return
false;
}
}
?>
2
tsmtgdi at gmail dot com
10 个月前
请注意,EXEC 会忽略 stderr!

所以类似

mkdir /impossible path

将失败,并且什么也不显示,您必须使用

mkdir /impossible 2>&1

才能正确捕获错误消息

否则
如果在 CLI 中执行,只会打印到终端,如果在浏览器中执行,就会丢失。

我强烈建议将这些注释添加到主要描述中,而不是丢失在底部的注释中。
45
Simon
9 年前
无法从您执行的命令中获取输出并显示在 $output 数组中吗?
它是否正在您的 shell 中回显所有内容?

在您命令的末尾追加“2>&1”,例如

exec("xmllint --noout ~/desktop/test.xml 2>&1", $retArr, $retVal);

将使用预期输出填充数组 $retArr;每行对应一个数组键。
27
msheakoski @t yahoo d@t com
20 年前
我也曾苦苦挣扎着让程序在 Windows 后台运行,而脚本继续执行。与其他解决方案不同,这种方法允许您以最小化、最大化或完全没有窗口的方式启动任何程序。llbra@phpbrasil 的解决方案确实有效,但它有时会在您真正想隐藏任务时在桌面上产生一个不必要的窗口。

在后台以最小化方式启动 Notepad.exe

<?php
$WshShell
= new COM("WScript.Shell");
$oExec = $WshShell->Run("notepad.exe", 7, false);
?>

在后台以不可见的方式启动 shell 命令
<?php
$WshShell
= new COM("WScript.Shell");
$oExec = $WshShell->Run("cmd /C dir /S %windir%", 0, false);
?>

以最大化方式启动 MSPaint,并在您关闭它之前继续执行脚本
<?php
$WshShell
= new COM("WScript.Shell");
$oExec = $WshShell->Run("mspaint.exe", 3, true);
?>

有关 Run() 方法的更多信息,请访问
http://msdn.microsoft.com/library/en-us/script56/html/wsMthRun.asp
9
Farhad Malekpour
17 年前
如果您尝试在使用信号 SIGCHLD 的脚本中使用 exec(即 pcntl_signal(SIGCHLD,'sigHandler');),它将返回 -1 作为命令的退出代码(尽管输出是正确的!)。要解决此问题,请删除信号处理程序,并在 exec 之后再次添加它。代码将类似于以下内容

...
pcntl_signal(SIGCHLD, 'sigHandler');
...
...
(更多代码、函数、类等)
...
...
// 现在通过 exec 执行命令
// 清除信号
pcntl_signal(SIGCHLD, SIG_DFL);
// 执行命令
exec('mycommand',$output,$retval);
// 将信号设置回我们的处理程序
pcntl_signal(SIGCHLD, 'sigHandler');
// 在此阶段,我们有 $retval 的正确值。

相同的解决方案也适用于 system 和 passthru。
4
Paul Sommer
8 年前
我尝试在 Windows 后台执行命令。
在花了数小时尝试了所有这些半成品示例后,我想分享我发现的有效语法(至少在 Windows 中如此)。这在 Linux 下没有测试,因为有更优雅的方法来生成进程。

基于 Arno van den Brink 的函数。

<?php
function execInBackground($cmd) {
if (
substr(php_uname(), 0, 7) == "Windows"){
pclose(popen("start /B ". $cmd, "r"));
}
else {
exec($cmd . " > /dev/null &");
}
}
?>

这与以下示例完美配合:
<?php
execInBackground
('del c:\tmp\*.*')
?>

但以下操作无效
<?php
execInBackground
('\"c:\path with spaces\my program.exe\"')
?>

为什么?
当 Windows 看到引号(")时,它认为这是窗口标题,而不是命令。
因此,当您的命令需要引号时,您必须先提供一个窗口名称,例如
execInBackground("\"title\"" "\"c:\path with spaces\my program.exe\")

窗口标题必须使用引号。否则 Windows 会认为这是程序名称。

很奇怪,但“嘿!这是 Windows!”:)
2
Bob-PHP at HamsterRepublic dot com
18 年前
exec 会从命令的输出中去除尾随空白字符。这使得无法捕获重要的空白字符。例如,假设一个程序输出制表符分隔的文本列,最后一列在某些行上包含空字段。尾随制表符很重要,但会被去除。

如果您需要保留尾随空白字符,则必须使用 popen()。
2
elwiz at 3e dot pl
13 年前
在 Windows-Apache-PHP 服务器上,同时多次使用 exec 命令会出现问题。如果同一个用户同时多次加载包含 exec 命令的脚本,服务器将会冻结。
在我的例子中,使用 exec 命令的 PHP 脚本用作图像标签的来源。在一个 HTML 中使用多个图像会导致服务器停止。
此问题在此处 (http://bugs.php.net/bug.php?id=44942) 有描述,以及解决方案 - 在执行 exec 命令之前停止会话,并在执行完毕后重新启动会话。

<?php

session_write_close
();
exec($cmd);
session_start();

?>
2
Hypolite Petovan
4 年前
在 Unix 上,要将命令 $cmd 在后台执行,唯一允许的标准输出重定向语法是“> /path/to/file &”。任何其他有效的标准输出重定向语法都无法让命令在后台运行。

<?php
// 以下代码将在后台运行
exec($cmd . " > /path/to/file &");

// 以下代码将不会在后台运行
exec($cmd . " >> /path/to/file &");
exec($cmd . " &> /path/to/file &");
exec($cmd . " &>> /path/to/file &");
exec($cmd . " 2>&1 > /path/to/file &");
exec($cmd . " 2>&1 >> /path/to/file &");
?>
2
hans at internit dot NO_SPAM dot com
22 年前
据我所知,没有办法使用 exec 函数将 Perl 数组传递回 PHP 脚本。

建议是在 Perl 脚本的末尾打印 Perl 数组变量,然后从 exec 函数返回的数组中获取每个数组成员。如果要传递多个数组,或者需要跟踪数组键和值,那么在 Perl 脚本的末尾打印每个数组或哈希变量时,应将值与键和数组名称连接起来,使用下划线,例如

foreach (@array) print "(array name)_(member_key)_($_)" ;

然后只需遍历 exec 函数返回的数组,并根据下划线分割每个变量。

在此,我特别感谢 Marat 的知识。希望这对其他寻找类似答案的人有所帮助!
2
php dot reg at kjpetrie dot co dot uk
1 年前
如果您想在后台运行命令,并使用 escapeshellcmd() 的建议,请确保您将重定向放在括号之外!这些是您希望 shell 解释的内容。

例如:exec(escapeshellcmd('mycommand and arguments').' > /dev/null &');
2
juan at laluca dot com
13 年前
我试图通过执行 cacls 并使用 PHP 在 Windows 环境下的 Apache 中对其进行解析,从远程计算机获取访问列表。

但是使用以下代码行,我没有得到任何内容,它被挂起了,虽然从命令行它运行得很好

<?php exec ('c:\\WINDOWS\\system32\\psexec.exe \\192.168.1.224 -u myuser -p mypassword -accepteula cacls c:\\documents\\RRHH && exit', $arrACL ); ?>

为了使其正常工作,我只需执行以下步骤
- 执行 services.msc 并找到 Apache 服务(在我的情况下是 wampapache)
- 右键单击 > 登录选项卡,并将本地系统帐户更改为创建的帐户,输入用户名和密码,然后重新启动服务。

(我将此用户添加到管理员组以避免权限问题,但这是不推荐的...)

它起作用了!它也可能适用于 IIS,所以如果您遇到相同的问题,请尝试一下....

希望这对某些人有所帮助,并对我的英语表示歉意
0
mamedul.github.io
9 个月前
使用 PHP 执行命令的跨函数解决方案-

function php_exec( $cmd ){

if( function_exists('exec') ){
$output = array();
$return_var = 0;
exec($cmd, $output, $return_var);
return implode( " ", array_values($output) );
}else if( function_exists('shell_exec') ){
return shell_exec($cmd);
}else if( function_exists('system') ){
$return_var = 0;
return system($cmd, $return_var);
}else if( function_exists('passthru') ){
$return_var = 0;
ob_start();
passthru($cmd, $return_var);
$output = ob_get_contents();
ob_end_clean(); // 使用此方法代替 ob_flush()
return $output;
}else if( function_exists('proc_open') ){
$proc=proc_open($cmd,
array(
array("pipe","r"),
array("pipe","w"),
array("pipe","w")
),
$pipes);
return stream_get_contents($pipes[1]);
}else{
return "@PHP_COMMAND_NOT_SUPPORT";
}

}
0
no at mail dot com
1 年前
<此注释从系统页面复制>
这是针对 WINDOWS 用户。我正在运行 Apache,我已经尝试了几个小时,但仍然无法捕获命令的输出。

我尝试了这里写的所有方法,然后继续在线搜索,但仍然没有结果。命令的输出从未被捕获。我得到的只是一个空数组。

最后,我在某个神奇家伙的博客评论中找到了解决方案。

将字符串 ' 2>&1' 添加到命令名称中,终于返回了输出!由于它使用流重定向将输出重定向到正确的位置,因此此方法适用于 PHP 中的 exec() 和 system()!
<?php
exec
("yourCommandName 2>&1", $output);
var_dump($output);
?>
2
Martin Lakes
13 年前
花了相当长的时间才弄清楚我接下来要发布的代码行。如果您想在后台执行命令,而不让脚本等待结果,您可以执行以下操作

<?php
passthru
("/usr/bin/php /path/to/script.php ".$argv_parameter." >> /path/to/log_file.log 2>&1 &");
?>

这里有一些重要的事情。

首先:将 PHP 二进制文件的完整路径放在其中,因为此命令将在 Apache 用户下运行,并且您可能没有在该用户中设置 php 这样的命令别名。

第二:注意命令字符串末尾的两个内容:'2>&1' 和 '&'。'2>&1' 用于将错误重定向到标准 IO。最重要的是命令字符串末尾的 '&',它告诉终端不要等待响应。

第三:确保您对 'log_file.log' 文件具有 777 权限

尽情享受!
0
krjdev at gmail dot com
2 年前
针对 OpenBSD 用户的说明

在 OpenBSD 的默认配置中,PHP 运行在一个 chroot 中。因此 exec() 命令将无法正常工作。您将收到一个 127(命令未找到)的结果代码。原因是 chroot 中缺少 shell(/bin/sh),但 exec() 命令需要 shell。

一个快速而肮脏的解决方案是在 chroot 目录中复制 /bin/sh(它被静态链接)
<?php
$ cp /bin/sh /var/www/bin
?>

(我在 OpenBSD 7.0 上使用 PHP 8.0.11 发现了这个问题。)
1
alvaro at demogracia dot com
13 年前
在 Windows 中,exec() 会发出对 "cmd /c your_command" 的内部调用。这意味着您的命令必须遵循 cmd.exe 强制执行的规则,包括在完整命令周围添加一组额外的引号

- http://ss64.com/nt/cmd.html

当前的 PHP 版本会考虑到这一点,并自动添加引号,但旧版本不会。

显然,该更改是在 PHP/5.3.0 中完成的,但没有向 5.2.x 反向移植,因为它是一个向后不兼容的更改。总结一下

- 在 PHP/5.2 和更早版本中,您必须将完整命令加参数用双引号括起来
- 在 PHP/5.3 和更高版本中,您不需要这样做(如果您这样做,您的脚本将崩溃)

如果您对内部机制感兴趣,以下是源代码

sprintf(cmd, "%s /c \"%s\"", TWG(comspec), command);

可以在 http://svn.php.net/viewvc/ 中找到它(请找到 php/php-src/trunk/TSRM/tsrm_win32.c,注释系统不允许直接链接)。
0
bahri at bahri dot info
15 年前
[danbrown AT php DOT net 补充说明:以下是一个 Linux 脚本,该脚本由本注释的贡献者建议放在名为 'pstools.inc.php' 的文件中,用于执行进程,检查进程是否存在,并通过 ID 杀死进程。灵感来自 https://php.net/exec#59428 中的 Windows 版本。]


<?php
function PsExecute($command, $timeout = 60, $sleep = 2) {
// 首先,执行进程,获取进程 ID

$pid = PsExec($command);

if(
$pid === false )
return
false;

$cur = 0;
// 其次,循环 $timeout 秒,检查进程是否正在运行
while( $cur < $timeout ) {
sleep($sleep);
$cur += $sleep;
// 如果进程不再运行,则返回 true;

echo "\n ---- $cur ------ \n";

if( !
PsExists($pid) )
return
true; // 进程必须已退出,成功!
}

// 如果进程在超时后仍在运行,则杀死进程并返回 false
PsKill($pid);
return
false;
}

function
PsExec($commandJob) {

$command = $commandJob.' > /dev/null 2>&1 & echo $!';
exec($command ,$op);
$pid = (int)$op[0];

if(
$pid!="") return $pid;

return
false;
}

function
PsExists($pid) {

exec("ps ax | grep $pid 2>&1", $output);

while( list(,
$row) = each($output) ) {

$row_array = explode(" ", $row);
$check_pid = $row_array[0];

if(
$pid == $check_pid) {
return
true;
}

}

return
false;
}

function
PsKill($pid) {
exec("kill -9 $pid", $output);
}
?>
0
dr_jones153 at hotmail dot com
16 年前
如果 SAFE_MODE 处于开启状态,并且您尝试通过在命令行末尾添加 "> /dev/null 2> /dev/null & echo $!" 来在后台运行脚本,则浏览器将一直挂起,直到脚本完成。

我的解决方案

创建一个 shell 脚本(例如 runscript.sh),其中包含您尝试在后台运行的脚本的执行行。
runscript.sh 通过一个不带重定向字符串的 exec() 调用运行,重定向字符串现在位于 runscript.sh 中。

runscript.sh 将几乎立即返回,因为原始脚本的输出被重定向,因此不会挂起您的浏览器,并且脚本在后台正常运行。
-1
ivk
3 年前
result_code -1 可能意味着“已达到文件描述符的最大数量”。检查 count(get_resources('stream')) 和 ulimit -Sn
-1
layton at layton dot tk
19 年前
这是第二次遇到这个问题,我认为其他人可能也会发现这个笔记有用。

我正在创建一个长时间运行的 exec'd 进程,我可以使用页面提交在 2 小时后访问它。问题是,当我第一次访问页面时,一切按预期工作。第二次,网页浏览器一直等待,但没有收到任何消息——CPU 时间没有受到影响,因此很明显有什么东西被阻塞了。

实际发生的情况是,所有打开的文件都被复制到 exec'd 进程中,包括网络连接。因此,当我第二次尝试访问网页时,我正在被给予旧的 http 网络连接,现在它被忽略了。

解决方案是扫描从 3 开始的所有文件句柄,并将它们全部关闭。请记住,句柄 0、1 和 2 分别是标准输入、标准输出和标准错误。
To Top