©
本文档使用 PHP中文网手册 发布
(PHP 4, PHP 5, PHP 7)
shell_exec — 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。
$cmd
)本函数同 执行操作符。
cmd
要执行的命令。
命令执行的输出。
如果执行过程中发生错误或者进程不产生输出,则返回 NULL
。
Note:
当进程执行过程中发生错误,或者进程不产生输出的情况下,都会返回
NULL
, 所以,使用本函数无法通过返回值检测进程是否成功执行。 如果需要检查进程执行的退出码,请使用 exec() 函数。
Example #1 shell_exec() 例程
<?php
$output = shell_exec ( 'ls -lart' );
echo "<pre> $output </pre>" ;
?>
Note:
当 PHP 运行在 安全模式 时,不能使用此函数。
[#1] jack dot harris-7ot2j4ip at yopmail dot Com [2014-09-11 13:52:11]
On Windows, if shell_exec does NOT return the result you expected and the PC is on an enterprise network, set the Apache service (or wampapache) to run under your account instead of the 'Local system account'. Your account must have admin privileges.
To change the account go to console services, right click on the Apache service, choose properties, and select the connection tab.
[#2] xxxpaul1104xxx at gmail dot com [2014-04-18 03:43:52]
//wish this could help for handle the shell_exec() output strings where "\n" that looks like a " " space.
//ex:
$configPath = "/home/somewhere";
$configList = shell_exec("ls $configPath/123.*.cfg"); // output is "/home/somewhere/123.111.cfg /home/somewhere/123.112.cfg"
// there looks like a "space" between output strings, but that's a "\n" actually.
$nodeList = trim(str_replace("\n",",",str_replace("$configPath/","",str_replace(".cfg","",$configList)))); // get "123.111,123.112"
[#3] ptitjib at hotmail dot fr [2012-10-26 10:45:58]
Run shell_exec powershell on IIS 7, PHP-Fast-CGI:
First of all, make sure you have set TPC protocol in IIS FastCGI Settings.
Next you can run shell_exec command as:
$output = shell_exec('powershell get-process < NUL');
echo $output;
Hope this help.
[#4] wally at soggysoftware dot co dot uk [2012-08-10 11:39:59]
As others have noted, shell_exec and the backtick operator (`) both return NULL if the executed command doesn't output anything.
This can be worked around by doing anything like the following:
shell_exec ("silentcmd && echo ' '");
Here we're simply outputting blank whitespace if the command succeeds - which satisfies this slightly strange issue. From there, you can trim() the command output etc.
[#5] trev at dedicate.co.uk [2011-10-21 07:34:11]
If you're trying to run a command such as "gunzip -t" in shell_exec and getting an empty result, you might need to add 2>&1 to the end of the command, eg:
Won't always work:
echo shell_exec("gunzip -c -t $path_to_backup_file");
Should work:
echo shell_exec("gunzip -c -t $path_to_backup_file 2>&1");
In the above example, a line break at the beginning of the gunzip output seemed to prevent shell_exec printing anything else. Hope this saves someone else an hour or two.
[#6] ilya at linemedia dot ru [2010-12-16 08:36:45]
sudo can be executed without storing pass in a file
system('echo "PASS" | sudo -u root -S COMMAND');
[#7] fabjoa dot mwoz at gmail dot com [2010-11-01 04:02:27]
I don't know how it goes in Windows, but in Linux, shell returns result with a line break at the end, I guess to make it more readable in shell, so don't forget to use trim() to encapsulate the returned result!
if(trim(shell_exec('whoami'))!='www-data')
[#8] nowayoutlive.de [2010-02-26 01:59:15]
Today I needed a piece of code, which makes use of the shell_exec function. For those who want to scan 2 letter domains too, here is a nice linux and php5 way, using a character generator class.
<?php
define("__USED_CHARS__", "abcdefghijklmnopqrstuvwxyz0123456789");
define("__CASE_SENSITIVE__", true); // Use string above or use uppercase / lowercase variant
$bf = new chargen(2); // new chargen object, length 2
$bf->generate("whois"); // generate chars and call whois function
function whois($str)
{
$domain = $str.".com";
$retval = shell_exec("whois $domain");
if (eregi("no match", $retval))
echo $domain." ist available\n";
else
echo $domain." is unavailable\n";
}
class chargen
{
private $chars = NULL;
private $maxlength = NULL;
protected $buffer = NULL;
function generate($mycallback = false)
{
foreach ($this->buffer as $char)
{
foreach ($this->chars as $nextchar)
{
$retval = $char.$nextchar;
$this->buffer[$retval] = $retval;
if ($mycallback && function_exists($mycallback))
$mycallback($retval);
else
echo $retval."\n";
}
}
if (strlen($retval) == $this->maxlength)
return;
$this->generate($mycallback);
}
function __construct($maxlength = 8)
{
$chars = array();
$this->buffer = array();
array_push($this->buffer, "");
for ($i = 0; $i < strlen(__USED_CHARS__); $i++)
{
$index = substr(__USED_CHARS__, $i, 1);
if (__CASE_SENSITIVE__)
{
$this->chars[$index] = $index;
}
else
{
$this->chars[strtolower($index)] = strtolower($index);
$this->chars[strtoupper($index)] = strtoupper($index);
}
}
$this->maxlength = $maxlength;
}
}
?>
[#9] martin at intelli-gens dot com [2010-02-17 03:21:16]
Also after lots of hair pulling why shell_exec didn't want to work for me I found out that in my case some things needed to be set (which normally are set by default).
the options -jo
-j means: don't recreate the paths found in the archive
-o means: always overwrite files
And I needed to specify the destination path (even though it should unzip in the same directory when not specified), this is done by -d [path]
The strange thing was that I didn't have to put these options when I would give the command on the command-line, only when I would call it with shell_exec.
So the complete command in php would be for me:
shell_exec('unzip -jo /path_to_archive/archive.zip -d /destination_path')
And putting en echo in front will help you a lot.
it should tell you something like this:
Archive: /path_to_zip/archive.zip
inflating: /destination_path/file1.jpeg
inflating: /destination_path/file2.jpeg
inflating: /destination_path/file3.jpeg
[#10] codeslinger at compsalot dot com [2010-01-13 18:47:24]
it took me a heck of a lot of head banging to finally solve this problem so I thought that I would mention it here.
If you are using Eclipse and you try to do something like
<?php
$out = shell_exec("php -s $File"); //this fails
?>
it will always fail when run inside of the Eclipse debugger. This happens on both Linux and Windows. I finally isolated the problem to changes that Eclipse makes to the environment when debugging.
The fix is to force the ini setting. If you don't need an ini then -n is sufficient.
<?php
$out = shell_exec("php -n -s $File"); //this works
?>
Of course if you run it outside of the debugger then it works fine without the -n. You may want to use a debug flag to control this behavior.
[#11] joelhy [2009-04-08 18:29:00]
Here is a easy way to grab STDERR and discard STDOUT:
add '2>&1 1> /dev/null' to the end of your shell command
For example:
<?php
$output = shell_exec('ls file_not_exist 2>&1 1> /dev/null');
?>
[#12] dburles@NOSPAMgmailDOTcom [2009-02-01 03:07:02]
Easy way to capture error output in windows
// we'll execute a php script as an example:
$out = shell_exec("php test.php 2> output");
print $out ? $out : join("", file("output"));
We assume in this case if the script produces output it has ran ok, the $out variable will then contain the output, if $out is empty then we read the captured error output from a file simply called 'output'.
Hope this helps someone
[#13] zd [2009-01-16 10:29:30]
It's probably worth noting that shell_exec() return value seems binary safe, while the output array from exec() is not.
In either case, a more controlled way to launch and interact with a process is proc_open() which allows you to have streams and is also binary safe.
[#14] Rgemini [2009-01-07 15:46:22]
A simple way to handle the problem of capturing stderr output when using shell-exec under windows is to call ob_start() before the command and ob_end_clean() afterwards, like this:
<?php
ob_start()
$dir = shell_exec('dir B:');
if is_null($dir)
{ // B: does not exist
// do whatever you want with the stderr output here
}
else
{ // B: exists and $dir holds the directory listing
// do whatever you want with it here
}
ob_end_clean(); // get rid of the evidence :-)
?>
If B: does not exist then $dir will be Null and the output buffer will have captured the message:
'The system cannot find the path specified'.
(under WinXP, at least). If B: exists then $dir will contain the directory listing and we probably don't care about the output buffer. In any case it needs to be deleted before proceeding.
[#15] dae3 [2008-12-22 02:53:47]
Beware of the following inconsistency: shell_exec() and the backtick operator will not return a string if the command's output is empty -- they'll return NULL instead.
This will make strict comparisons to '' return false.
[#16] redtuugii at yahoo dot com [2008-11-12 03:30:15]
if you want get exit status
<?php
$command = 'ls -lpas';
// Execute the shell command
$shellOutput = shell_exec($command.' > /dev/null; echo $?');
//return execute status;
echo trim($shellOutput);
?>
[#17] sa577 at gre dot ac dot uk [2008-08-14 16:59:15]
The problem described by 'concept at conceptonline dot hu' was mine too (writing the output on the Apache error log instead of returning it). But I think the cause explanation is misleading: it is not due to running script in a different path. The clue for this behaviour as Apache documentation mention is:
"A very wide variety of different messages can appear in the error log. Most look similar to the example above. The error log will also contain debugging output from CGI scripts. Any information written to <i>stderr</i> by a CGI script will be copied directly to the error log." (http://httpd.apache.org/docs/1.3/logs.html#errorlog)
So the most probable cause for this problem is a running script which tends to write its output on stderr instead of stdout. You cannot even capture it by piping (e.g. [command]>[output] syntax of unix).
The smart trick to get rid of this situation (that can become a serious one as I experienced) is one suggested by Anatol Pomozov in his currently non-accessible weblog (I saw his post on archive.com!):
You should add after your command these characters within the qoutation mark:
" 2>&1"
I guess it forces OS to copy stderr on stdout.
[#18] ben at thelocust dot org [2008-08-08 10:59:17]
For those running PHP on IIS under a 64-bit system, but running IIS in 32-bit mode, you will need to grant the IUSR_<machinename> user Read & Execute / Read permissions not on the C:\WINDOWS\SYSTEM32\cmd.exe file, but instead on the C:\WINDOWS\SysWOW64\cmd.exe - which is the 32-bit version of the command prompt.
[#19] saivert at saivert dot com [2008-05-12 20:48:21]
How to get the volume label of a drive on Windows
<?php
function GetVolumeLabel($drive) {
// Try to grab the volume name
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");
?>
Note: The regular expression assumes a english version of Windows is in use. modify it accordingly for a different localized copy of Windows.
[#20] Anonymous [2008-04-08 08:35:17]
In response to nathandehert and Paul Cook, rather than sudoing to execute a command, you might want to abstract the command(s) into a yet another script (php, perl, bash, whatever) and make use of the setuid bit. then just execute that script from php. of course, bear in mind the security implications of setuid.
[#21] gabekon at gmail dot com [2008-04-03 08:33:02]
RE: kamermans note,
I was having a similar problem with the PATH variable when using shell_exec. Even with a hard-coded full path to a binary, I also got an error about a .so file that could not be found. After some reading up, I realized I had to set the LD_LIBRARY_PATH variable:
<?php
$command = 'export LD_LIBRARY_PATH="' . $path_to_library_dir .'"; ' . $path_to_binary;
shell_exec($command);
?>
Hope this saves someone a headache,
- G
[#22] guenter at grodotzki dot ph [2008-01-16 08:07:11]
If the return is empty, then command could not be executed or executed command returned an error.
[#23] RoBorg [2007-11-08 07:45:46]
The Subversion error "svn: Can't recode string" can be caused by the locale being wrong. Try
<?php
putenv('LANG=en_US.UTF-8');
?>
(or whatever your preferred locale is) before you call shell_exec()
[#24] kamermans at teratechnologies dot net [2007-10-15 08:01:04]
I'm not sure what shell you are going to get with this function, but you can find out like this:
<?php
$cmd = 'set';
echo "<pre>".shell_exec($cmd)."</pre>";
?>
On my FreeBSD 6.1 box I get this:
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='
'
Very interesting. Note that the PATH may not be as complete as you need. I wanted to run Ghostscript via ImageMagik's "convert" and ended up having to add my path before running the command:
<?php
$cmd = 'export PATH="/usr/local/bin/"; convert -scale 25%x25% file1.pdf[0] file2.png 2>&1';
echo "<pre>".shell_exec($cmd)."</pre>";
?>
ALSO, note that shell_exec() does not grab STDERR, so use "2>&1" to redirect it to STDOUT and catch it.
[#25] eric dot peyremorte at iut-valence dot fr [2007-10-05 02:01:41]
I had trouble with accented caracters and shell_exec.
ex :
Executing this command from 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
gave me that :
Vid??os D 0 Tue Jun 12 14:41:21 2007
Desktop DH 0 Mon Jun 18 17:41:36 2007
Using php like that :
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")
gave me that :
Vid Desktop DH 0 Mon Jun 18 17:41:36 2007
The two lines were concatenated from the place where the accent was.
I found the solution : php execute by default the command with LOCALE=C.
I just added the following lines before shell_exec and the problem was solved :
$locale = 'fr_FR.UTF-8';
setlocale(LC_ALL, $locale);
putenv('LC_ALL='.$locale);
Just adapt it to your language locale.
[#26] Woody/mC [2007-07-12 07:07:01]
regarding the "ping" test that was mentioned by andy25it at hotmail dot it:
this might not work if the target server is behind a firewall which drops icmp packets. a better approach would be the use of fsockopen(), which doesn't use icmp. in the notes section of the function list, Alexander Wegener wrote a nice implementation of an isOnline() function which works with both http and https.
[#27] d-skyman at gmx dot de [2007-04-08 10:43:27]
Got it! That was a "how does the windows scripting host work" problem.
Here's the answer:
<?php
$runCommand = "C:\\WINDOWS\\system32\\shutdown.exe -t:30"; //Wrong by purpuse to get some good output
$WshShell = new COM("WScript.Shell");
$output = $WshShell->Exec($runCommand)->StdOut->ReadAll;
echo "<p>$output</p>";
?>
[#28] RayJ [2007-02-25 21:07:04]
After many failed attempts to find a way to run a php script in the background (the script had to be activated from a web browser). Since I create and test my scripts on a windows box, I need an automatic way to detect if the script was running on windows or the Linux server. The activated script will continue to run even if you close your browser.
<?php
$runCommand = 'php -q FULLPATH/FILE.php';
if(isset($_SERVER['PWD'])/
class exec {
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);
}
function is_running($PID){
exec("ps $PID", $ProcessState);
return(count($ProcessState) >= 2);
}
function kill($PID){
if(exec::is_running($PID)){
exec("kill -KILL $PID");
return true;
}else return false;
}
};
?>
[#40] php [AT] jsomers [DOT] be [2005-09-27 06:00:59]
<?php
class KillAllProcesses {
function killallprocesses() {
$this->listItems();
}
function listItems() {
$output = shell_exec('ps -x');
$this->output($output);
// Put each individual line into an array
$array = explode("\n", $output);
$this->doKill($array);
}
function output($output) {
print "<pre>".$output."</pre>";
}
function doKill($array) {
for ($i = 1; $i < count($array); $i++) {
$id = substr($array[$i], 0, strpos($array[$i], ' ?'));
shell_exec('kill '.$id);
}
}
}
new KillAllProcesses();
?>
It's not the very best solution, but I've used it a couple of times when I needed to do it quick without to much trouble.
Make not I kill all the processes, on my server px -x will only return like 4 times /sbin/apache and it's pretty safe to kill them without any trouble.
[#41] concept at conceptonline dot hu [2005-09-19 14:02:04]
Interestingly, if you execute a script which is not in your path (or you have made a typo, or if the script does no exist at all), you will get no return value. The error will be logged into the error_log of your webserver.
Someone could add a note how this can be (if it could be) overriden, as the standard behaviour is not really fool-proof.
[#42] ludvig dot ericson at gmail dot com [2005-09-01 11:50:29]
(if you have any other way of doing this PLEASE send me a mail)
(The following note is interesting to *NIX administrators ONLY)
Jailing a PHP CLI session is not the easiest thing to do.
PHP has a gigantic list of dependencies, you can check it by
ldd `which php`
Each of these .so files (shared libraries) can have dependencies, and can depend on each other, making the list much bigger.
However, there are tools out there to copy all dependencies into a jailed directory, you just have to search for them - and I had no luck.
What I did was just to run these few commands:
su
mkdir /jailpath && mkdir /jailpath/lib
cp /lib
$size
, boolean $preserve
)$class
)$run
[, Closure $construct
[, array $args
]] )$from
[, mixed $overwrite
] )$block
[, mixed $...
] )$timeout
] )[#1] 312036773 at qq dot com [2015-11-11 07:58:40]
<?php
class Wallet{
public $balance;
public function __construct($money){
$this->balance = $money;
}
public function getBalance(){
return $this->balance;
}
public function setBalance($value){
$this->balance = $value;
}
}
class MyThread extends Thread{
private $wallet;
private $std;
public function __construct($wallet,$std){
$this->wallet = $wallet;
$this->std = $std;
}
public function run(){
$this->synchronized(function($thread){
$hack = $this->wallet;
if($hack->getBalance() - 80 >0){
sleep(1);
$hack->setBalance($hack->getBalance() - 80);
echo $this->getThreadId() . "reduce 80 successful<br/>Current num is??" . $hack->getBalance() . "<Br/>";
//Here is Wrong! The result is bool(false)????!!!!
var_dump($hack == $this->wallet);
}
else
echo $this->getThreadId() . "reduce fail<br/>Current num is??" . $hack->getBalance() . "<br/>";
},$this->std);
}
}
$wallet = new Wallet(200);
$std = new stdClass();
for($x=0;$x<3;$x++){
$pool[] = new MyThread($wallet,$std);
$pool[$x]->start();
}
[#2] derkontrollfreak+php at gmail dot com [2015-07-12 07:50:35]
> Threaded objects, most importantly, provide implicit safety for the programmer; all operations on the object scope are safe.
However, this comes with a trade-off: Operations on the instance scope are slower than on plain PHP objects.
Static properties are not affected as they are thread-local.