shell_exec

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

shell_exec通过 shell 执行命令并返回完整的输出作为字符串

描述

shell_exec(string $command): string|false|null

此函数与 反引号运算符 相同。

注意:

在 Windows 上,底层管道以文本模式打开,这会导致该函数对于二进制输出失败。对于此类情况,请考虑使用 popen()

参数

command

要执行的命令。

返回值

一个包含执行命令输出的 string,如果管道无法建立则为 false,如果发生错误或命令未产生任何输出则为 null

注意:

此函数在发生错误或程序未产生任何输出时都可以返回 null。无法使用此函数检测执行失败。exec() 应在需要访问程序退出代码时使用。

错误/异常

当管道无法建立时,将生成 E_WARNING 级别的错误。

示例

示例 #1 一个 shell_exec() 示例

<?php
$output
= shell_exec('ls -lart');
echo
"<pre>"$output</pre>";
?>

参见

添加注释

用户贡献的注释 36 个注释

trev at dedicate.co.uk
12 年前
如果您尝试在 shell_exec 中运行像 "gunzip -t" 这样的命令并得到空结果,您可能需要在命令末尾添加 2>&1,例如

不一定有效
echo shell_exec("gunzip -c -t $path_to_backup_file");

应该有效
echo shell_exec("gunzip -c -t $path_to_backup_file 2>&1");

在上面的示例中,gunzip 输出开头的一个换行符似乎阻止了 shell_exec 打印任何其他内容。希望这能为其他人节省一两个小时。
alexandre dot schmidt at gmail dot com
8 年前
要将命令在后台运行,输出必须重定向到 /dev/null。这在 exec() 手册页中写明。但是,在某些情况下,您可能需要将输出记录到其他地方。将输出重定向到文件,像这样,对我不起作用

<?php
# 这不起作用!
shell_exec("my_script.sh 2>&1 >> /tmp/mylog &");
?>

使用上述命令仍然会挂起网页浏览器的请求。

看来您必须将 "dev/null" 确切地添加到命令行中。例如,这有效

<?php
# 有效,但输出会丢失
shell_exec("my_script.sh 2>/dev/null >/dev/null &");
?>

但我想要输出,所以我使用了这个

<?php
shell_exec
("my_script.sh 2>&1 | tee -a /tmp/mylog 2>/dev/null >/dev/null &");
?>

希望这能帮到其他人。
Rgemini
15 年前
在 Windows 下使用 shell-exec 时,处理捕获 stderr 输出问题的一种简单方法是在命令之前调用 ob_start(),并在之后调用 ob_end_clean(),如下所示

<?php
ob_start
()
$dir = shell_exec('dir B:');
if
is_null($dir)
{
// B: 不存在
// 在这里对 stderr 输出做任何你想做的事情
}
else
{
// B: 存在,$dir 包含目录列表
// 在这里对其做任何你想做的事情
}
ob_end_clean(); // 删除证据 :-)
?>

如果 B: 不存在,则 $dir 将为 Null,输出缓冲区将捕获消息

'找不到指定路径'。

(至少在 WinXP 下是这样)。如果 B: 存在,则 $dir 将包含目录列表,我们可能不关心输出缓冲区。在任何情况下,都需要在继续之前将其删除。
smcbride at msn dot com
3 年前
从 PHP 7.4 开始,proc_open 可能是大多数用例的更好解决方案。它提供了更好的控制和平台独立性。如果您仍然想使用 shell_exec(),我喜欢用一个允许更好的控制的函数来包装它。

类似下面的代码解决了 Apache/PHP 中一些后台进程问题。它还

public function sh_exec(string $cmd, string $outputfile = "", string $pidfile = "", bool $mergestderror = true, bool $bg = false) {
$fullcmd = $cmd;
if(strlen($outputfile) > 0) $fullcmd .= " >> " . $outputfile;
if($mergestderror) $fullcmd .= " 2>&1";
if($bg) {
$fullcmd = "nohup " . $fullcmd . " &";
if(strlen($pidfile)) $fullcmd .= " echo $! > " . $pidfile;
} else {
if(strlen($pidfile) > 0) $fullcmd .= "; echo $$ > " . $pidfile;
}
shell_exec($fullcmd);
}
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";
}

}
eric dot peyremorte at iut-valence dot fr
16 年前
我在带重音符号的字符和 shell_exec 上遇到了麻烦。

例如

从 shell 执行此命令

/usr/bin/smbclient '//BREZEME/peyremor' -c 'dir' -U 'peyremor%*********' -d 0 -W 'ADMINISTRATIF' -O 'TCP_NODELAY IPTOS_LOWDELAY SO_KEEPALIVE SO_RCVBUF=8192 SO_SNDBUF=8192' -b 1200 -N 2>&1

给了我这个

Vidéos D 0 Tue Jun 12 14:41:21 2007
Desktop DH 0 Mon Jun 18 17:41:36 2007

使用 PHP 像这样

shell_exec("/usr/bin/smbclient '//BREZEME/peyremor' -c 'dir' -U 'peyremor%*******' -d 0 -W 'ADMINISTRATIF' -O 'TCP_NODELAY IPTOS_LOWDELAY SO_KEEPALIVE SO_RCVBUF=8192 SO_SNDBUF=8192' -b 1200 -N 2>&1")

给了我这个

Vid Desktop DH 0 Mon Jun 18 17:41:36 2007

两条线从重音所在的位置拼接在一起。

我找到了解决方法:php 默认使用 LOCALE=C 执行命令。

我在 shell_exec 之前添加了以下几行代码,问题就解决了

$locale = 'fr_FR.UTF-8';
setlocale(LC_ALL, $locale);
putenv('LC_ALL='.$locale);

只需将其调整为你的语言区域设置即可。
哪个 shell?
2 年前
任何地方都提到了 shell_exec() 运行的哪个 SHELL 吗?...在我的系统上,它运行的是 sh,而不是 bash,这会导致 <() 进程替换失败,且没有错误消息。

文档不应该提到此函数 (& 反引号 ``) 运行的是哪个 shell 吗?
kamermans at teratechnologies dot net
16 年前
我不确定你将使用此函数获得哪个 shell,但你可以像这样找出

<?php
$cmd
= 'set';
echo
"<pre>".shell_exec($cmd)."</pre>";
?>

在我的 FreeBSD 6.1 机器上,我得到了以下结果

USER=root
LD_LIBRARY_PATH=/usr/local/lib/apache2
HOME=/root
PS1='$ '
OPTIND=1
PS2='> '
LOGNAME=root
PPID=88057
PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin
SHELL=/bin/sh
IFS='
'

非常有趣。请注意,PATH 可能并不像你需要的那么完整。我想通过 ImageMagik 的“convert”运行 Ghostscript,结果发现我必须在运行命令之前添加我的路径

<?php
$cmd
= 'export PATH="/usr/local/bin/"; convert -scale 25%x25% file1.pdf[0] file2.png 2>&1';
echo
"<pre>".shell_exec($cmd)."</pre>";
?>

此外,请注意,shell_exec() 不会获取 STDERR,因此使用“2>&1”将其重定向到 STDOUT 并捕获它。
RoBorg
16 年前
Subversion 错误“svn: Can't recode string”可能是由于区域设置错误造成的。尝试
<?php
putenv
('LANG=en_US.UTF-8');
?>
(或你首选的区域设置) 在调用 shell_exec() 之前。
joelhy
15 年前
以下是一种简单的方法来获取 STDERR 并丢弃 STDOUT
在 shell 命令的末尾添加 '2>&1 1> /dev/null'

例如
<?php
$output
= shell_exec('ls file_not_exist 2>&1 1> /dev/null');
?>
jack dot harris-7ot2j4ip at yopmail dot Com
9 年前
在 Windows 上,如果 shell_exec 没有返回你预期的结果,并且 PC 位于企业网络上,请将 Apache 服务(或 wampapache)设置为以你的帐户而不是“本地系统帐户”运行。你的帐户必须具有管理员权限。

要更改帐户,请转到控制台服务,右键单击 Apache 服务,选择属性,然后选择连接选项卡。
codeslinger at compsalot dot com
14 年前
我花了很长时间才解决这个问题,所以我认为应该在这里提一下。

如果你使用的是 Eclipse,并且尝试执行以下操作

<?php

$out
= shell_exec("php -s $File"); //这会失败
?>

它在 Eclipse 调试器中运行时始终会失败。这在 Linux 和 Windows 上都会发生。我最终将问题隔离为 Eclipse 在调试时对环境所做的更改。

解决方法是强制使用 ini 设置。如果你不需要 ini,则 -n 就足够了。

<?php

$out
= shell_exec("php -n -s $File"); //这会成功
?>

当然,如果你在调试器之外运行它,则它在没有 -n 的情况下也能正常工作。你可能希望使用一个调试标志来控制这种行为。
tonysb at gmx dot net
22 年前
shell_exec 非常有用,它可以替代在不可用时使用的 virtual() 函数(例如 Microsoft IIS)。你只需删除标题中发送的内容类型字符串即可

<?
$mstrng = shell_exec('yourcgiscript.cgi');
$mstrng = ereg_replace( "Content-type: text/html", "", $mstrng );
echo $mstrng;
?>

这对我有用,可以替代 SSI 或 virtual() 函数。

Anton Babadjanov
http://www.vbcn.com.ar
saivert at saivert dot com
16 年前
如何在 Windows 上获取驱动器的卷标

<?php

function GetVolumeLabel($drive) {
// 尝试获取卷名称
if (preg_match('#Volume in drive [a-zA-Z]* is (.*)\n#i', shell_exec('dir '.$drive.':'), $m)) {
$volname = ' ('.$m[1].')';
} else {
$volname = '';
}
return
$volname;
}

print
GetVolumeLabel("c");

?>

注意:正则表达式假设使用的是英文版本的 Windows。针对不同语言版本的 Windows,请相应地修改它。
pedroxam at gmail dot com
5 年前
这是我的建议

function execCommand($command, $log) {

if (substr(php_uname(), 0, 7) == "Windows")
{
//windows
pclose(popen("start /B " . $command . " 1> $log 2>&1", "r"));
}
else
{
//linux
shell_exec( $command . " 1> $log 2>&1" );
}

return false;
}
匿名
19 年前
注意如何提升 php 脚本的权限。最好谨慎行事并做好计划。很容易就会出现巨大的安全漏洞。以下是一些我从实验和 Unix 文档中收集到的有用提示。

需要考虑的事情

1. 如果你在 Unix 中将 php 作为 Apache 模块运行,那么你运行的每个系统命令都会以 apache 用户的身份运行。这是有道理的...Unix 不允许以这种方式提升权限。如果你需要以提升的权限运行系统命令,请仔细考虑这个问题!

2. 如果你决定以 root 身份运行 apache,那你简直是疯了。总会有更好的方法。

3. 如果你决定使用 SUID,最好不要对脚本使用 SUID。在许多 Unix 版本上,SUID 对脚本是禁用的。SUID 脚本会打开安全漏洞,因此你并不总是希望走这条路,即使它是一个选项。编写一个简单的二进制文件,并将二进制文件的权限提升为 SUID。在我看来,通过 SUID 传递系统命令(即让 SUID 接受命令名称作为参数)是一个糟糕的主意。你可能不如直接以 root 身份运行 Apache!
rustleb at hotmail dot com
18 年前
为了捕获 stdout 和 stderr,当你不在乎中间文件时,我使用以下方法获得了更好的结果...
<?php
function cmd_exec($cmd, &$stdout, &$stderr)
{
$outfile = tempnam(".", "cmd");
$errfile = tempnam(".", "cmd");
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("file", $outfile, "w"),
2 => array("file", $errfile, "w")
);
$proc = proc_open($cmd, $descriptorspec, $pipes);

if (!
is_resource($proc)) return 255;

fclose($pipes[0]); //实际上并不想提供任何输入

$exit = proc_close($proc);
$stdout = file($outfile);
$stderr = file($errfile);

unlink($outfile);
unlink($errfile);
return
$exit;
}
?>

这与重定向没有什么不同,只是它会为你处理临时文件(你可能需要将目录从“.”更改),并且由于 proc_close 调用,它会自动阻塞。这模拟了 shell_exec 的行为,并且还为你提供了 stderr。
shell master
9 个月前
你可以使用一行代码来打印 shell 脚本的输出,如下所示

<?= shell_exec("/proof.sh"); ?>
Numabyte
3 年前
需要注意的一点是,在某些情况下,例如在 MacOS 上,使用“2>&1”来正确输出 exec() 的字符串,当你在命令周围加上括号时会效果很好。

例如
在命令周围加上括号
$cmd = "(mysqldump -h l ocalhost -u root -ppassword --databases login_practice_db > /Users/username/Desktop/testDB.sql) 2>&1";

$sys = shell_exec($cmd ); //有效,并产生错误(localhost 故意写错了空格)

不在命令周围加上括号
$cmd = "mysqldump -h l ocalhost -u root -ppassword --databases login_practice_db > /Users/username/Desktop/testDB.sql 2>&1";

$sys = shell_exec($cmd ); //什么也不做,并变为空白,就好像结果没有错误一样。空字符串。

有趣的是,shell 命令的执行可以归结为对字符串解析的细微差别。
wally at soggysoftware dot co dot uk
12 年前
正如其他人所指出的,如果执行的命令没有输出任何内容,则 shell_exec 和反引号运算符 (`) 都将返回 NULL。

可以通过以下方法解决此问题

shell_exec ("silentcmd && echo ' '");

这里,我们只是在命令成功的情况下输出空白,这满足了这个稍微奇怪的问题。从那里,您可以修剪() 命令输出等。
jessop <AT> bigfoot <DOT> com
20 年前
对于那些试图在类 Unix 平台上使用 shell_exec 并似乎无法使其工作的人,只是提醒一下。PHP 以系统上的 Web 用户身份执行(通常对于 Apache 来说是 www),因此您需要确保 Web 用户对您试图在 shell_exec 命令中使用的任何文件或目录具有权限。否则,它看起来什么也不做。
smithnigelw at gmail dot com
6 年前
在 Windows 上使用 PHP 时,如果您收到“警告:shell_exec() [function.shell-exec]:无法执行”错误,则需要检查文件“C:\WINDOWS\system32\cmd.exe”的权限。您需要对该文件具有读/执行权限。建议使用 sysinternals Process Monitor “procmon.exe” 来确认尝试运行“cmd.exe”的用户。在“进程名称”上过滤为“php-cgi.exe”,并且“路径”以“cmd.exe”结尾。查看具有访问拒绝错误的任务的事件属性,它将显示“模拟”用户名。这通常是“Internet Guest Account”,通常是“NT AUTHORITY\IUSR”。
gabekon at gmail dot com
16 年前
关于:kamermans 的备注,

我在使用 shell_exec 时遇到了与 PATH 变量类似的问题。即使对二进制文件使用硬编码的完整路径,我也收到了关于无法找到 .so 文件的错误。在阅读了一些资料后,我意识到我必须设置 LD_LIBRARY_PATH 变量

<?php

$command
= 'export LD_LIBRARY_PATH="' . $path_to_library_dir .'"; ' . $path_to_binary;
shell_exec($command);

?>

希望这能省去一些人的麻烦,

- G
ruan at nospam dot tillcor dot com
17 年前
在遵循 Kenneth 的方法通过此页面上提到的 nanoweb 服务器执行 root 脚本时,您很可能需要能够运行像 lynx 这样的文本模式浏览器并将 php 脚本传递给它(效果很好)。

在苦苦挣扎了一段时间后(lynx 总是要求我下载文件而不是执行它),我意识到我必须另外安装 php-cgi 并修改 nanoweb 配置文件以使用该 php 解释器而不是 /usr/bin/php。(在 Debian 上,这是 CLI 版本)。

在 Ubuntu 6.06 上

apt-get install php5-cgi

编辑 /etc/nanoweb/nanoweb.conf 并快速重新启动 Web 服务器后,lynx 和 links 将正确执行您的 PHP 脚本。

希望这能帮助其他遇到同样问题的人 :)

Ruan Fourie
phillipberry at NOSPAM dot blisswebhosting dot com
19 年前
我发现了一些奇怪的事情。

如果您运行 exec,然后紧跟着 shell_exec,则 shell_exec 根本不会运行,并将返回 NULL。

为了使其正常工作,我在 exec 后面加了一个 sleep(5),现在 shell_exec 可以正常工作了。
Martin Rampersad
20 年前
我有 PHP(CGI)和 Apache。我还使用 shell_exec() 执行 shell 脚本,这些脚本使用 PHP CLI。这种组合破坏了从调用返回的字符串值。我得到了二进制垃圾。以 #!/usr/bin/bash 开头的 shell 脚本会正确返回其输出。

解决方法是强制使用干净的环境。PHP CLI 不再具有 CGI 环境变量来阻塞。

<?php

// 二进制垃圾。
$ExhibitA = shell_exec('/home/www/myscript');

// 完美。
$ExhibitB = shell_exec('env -i /home/www/myscript');

?>

-- start /home/www/myscript
#!/usr/local/bin/phpcli
<?php

echo("Output.\n");

?>
-- end /home/www/myscript
mcbeth at broggs dot org
21 年前
关于最后一个示例的错误检查。许多 shell 都有 && 运算符,因此您可以使用它将命令串联在一起,而不是使用 ; 如果任何时候任何程序失败,您将在执行剩余部分之前返回
Paul Cook
17 年前
Nathan De Hert 在下面提到的技术相当不安全——您永远不应该将密码放在 Apache 用户可以读取的文件中。

如果您在 *nix 系统上需要这种功能,请查看 /etc/sudo 文件(使用命令“visudo”编辑)。NOPASSWD 标签允许指定用户以 root 身份运行指定命令,而无需指定密码。这是一些额外的配置,但更安全。
martin at intelli-gens dot com
14 年前
此外,在经历了大量的头发拔掉之后,我发现 shell_exec 无法为我工作的原因是我需要设置某些东西(这些东西通常默认设置)。

选项 -jo
-j 意味着:不要重新创建档案中找到的路径
-o 意味着:始终覆盖文件

我还需要指定目标路径(即使没有指定,它也应该解压到同一个目录中),这可以通过 -d [路径] 来实现

奇怪的是,当我从命令行给出命令时,我不必添加这些选项,只有当使用 shell_exec 调用它时才需要添加。

因此,在我这里,php 中的完整命令将是

shell_exec('unzip -jo /path_to_archive/archive.zip -d /destination_path')

在前面加上一个 echo 将对你帮助很大。
它应该告诉你类似这样的东西


Archive: /path_to_zip/archive.zip
inflating: /destination_path/file1.jpeg
inflating: /destination_path/file2.jpeg
inflating: /destination_path/file3.jpeg
dburles@NOSPAMgmailDOTcom
15 年前
在 Windows 中捕获错误输出的简单方法

// 我们将执行一个 php 脚本作为示例
$out = shell_exec("php test.php 2> output");
print $out ? $out : join("", file("output"));

我们假设在这种情况下,如果脚本产生输出,它已经成功运行,则 $out 变量将包含输出,如果 $out 为空,则我们从一个名为“output”的文件中读取捕获的错误输出。

希望这能帮助到某人
rigsbr at yahoo dot com dot br
18 年前
如果您需要在没有权限的情况下执行命令,并且无法通过 ssh 执行它或安装任何扩展,那么在 Apache 1.3.x 和 PHP 4 中有一种方法。
在 cgi-bin 目录中创建一个文件,如下所示

#!/usr/bin/php
<?
echo shell_exec('whoami');
?>

不要忘记设置您创建的文件的权限以执行它。因此,从浏览器中调用它,您将看到该脚本将由 shell 用户执行,而不是用户 nobody(如果运行 PHP 脚本,则是 apache 默认用户)。
Ashraf Kaabi
18 年前
我写了一个用于在后台运行、杀死 PID、检查是否正在运行的完整类

<?php
/**
* @author Ashraf M Kaabi
* @name Advance Linux Exec
*/
class exec {
/**
* 在后台运行应用程序
*
* @param unknown_type $Command
* @param unknown_type $Priority
* @return PID
*/
function background($Command, $Priority = 0){
if(
$Priority)
$PID = shell_exec("nohup nice -n $Priority $Command > /dev/null & echo $!");
else
$PID = shell_exec("nohup $Command > /dev/null & echo $!");
return(
$PID);
}
/**
* 检查应用程序是否正在运行!
*
* @param unknown_type $PID
* @return boolen
*/
function is_running($PID){
exec("ps $PID", $ProcessState);
return(
count($ProcessState) >= 2);
}
/**
* 杀死应用程序 PID
*
* @param unknown_type $PID
* @return boolen
*/
function kill($PID){
if(
exec::is_running($PID)){
exec("kill -KILL $PID");
return
true;
}else return
false;
}
};
?>
James McCormack
20 年前
我有一个 perl 程序,它从命令行运行良好,但不能使用 shell_exec()、exec() 或 system() - 没有任何东西被返回。我使用的是完整路径,并且权限已正确设置。

结果表明,Perl 程序使用的内存超过了 PHP.INI 文件设置的允许值。增加“memory_limit”解决了问题。
concept at conceptonline dot hu
18 年前
有趣的是,如果您执行一个不在您的路径中的脚本(或者您输入错误,或者脚本根本不存在),您将不会得到任何返回值。错误将记录到您的 Web 服务器的 error_log 中。

有人可以添加一个说明如何覆盖此行为(如果可以)的注释,因为标准行为并不真正防错。
php [AT] jsomers [DOT] be
18 年前
<?php

/**
* PHP 终止进程
*
* 有时,脚本可能会在不应该停止的情况下继续运行,并且在关闭浏览器或关闭计算机后不会停止。因为并不总是容易使用 SSH,所以有一个解决方法。
*
* @author Jensen Somers <[email protected]>
* @version 1.0
*/

class KillAllProcesses {
/**
* 构造类
*/
function killallprocesses() {
$this->listItems();
}

/**
* 列出所有项目
*/
function listItems() {
/*
* PS Unix 命令用于报告进程状态
* -x 选择没有控制 tty 的进程
*
* 输出将类似于:
* 16479 pts/13 S 0:00 -bash
* 21944 pts/13 R 0:00 ps -x
*
*/
$output = shell_exec('ps -x');

$this->output($output);

// 将每行单独放入数组中
$array = explode("\n", $output);

$this->doKill($array);
}

/**
* 打印进程列表
* @param string $output
*/
function output($output) {
print
"<pre>".$output."</pre>";
}

/**
* 终止所有进程
* 应该可以在这里进行过滤,但我现在不会这样做。
* @param array $array
*/
function doKill($array) {
/*
* 因为我们 $output 的第一行看起来像
* PID TTY STAT TIME COMMAND
* 我们会跳过这一行。
*/
for ($i = 1; $i < count($array); $i++) {
$id = substr($array[$i], 0, strpos($array[$i], ' ?'));
shell_exec('kill '.$id);
}
}
}

new
KillAllProcesses();

?>

这不是最好的解决方案,但我用过几次,当需要快速完成而无需太多麻烦时。
请注意,我终止了所有进程,在我的服务器上,px -x 只会返回大约 4 次 /sbin/apache,并且杀死它们是非常安全的,不会有任何问题。
ricky at rocker dot com
5 年前
尽管

shell_exec("env")

返回

SHELL=/bin/bash

我只是无法获得

shell_exec("bash command <(another bash command)")

运行。我现在通过使用以下命令使其工作:

shell_exec("bash -c \"bash command <(another bash command)\"")
To Top