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>";
?>

参见

add a note add a note

User Contributed Notes 36 notes

up
236
trev at dedicate.co.uk
13 years ago
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.
up
46
alexandre dot schmidt at gmail dot com
8 years ago
To run a command in background, the output must be redirected to /dev/null. This is written in exec() manual page. There are cases where you need the output to be logged somewhere else though. Redirecting the output to a file like this didn't work for me:

<?php
# this doesn't work!
shell_exec("my_script.sh 2>&1 >> /tmp/mylog &");
?>

Using the above command still hangs web browser request.

Seems like you have to add exactly "/dev/null" to the command line. For instance, this worked:

<?php
# works, but output is lost
shell_exec("my_script.sh 2>/dev/null >/dev/null &");
?>

But I wanted the output, so I used this:

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

Hope this helps someone.
up
15
Rgemini
15 years ago
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.
up
5
smcbride at msn dot com
3 years ago
proc_open is probably a better solution for most use cases as of PHP 7.4.  There is better control and platform independence.  If you still want to use shell_exec(), I like to wrap it with a function that allows better control.

Something like below solves some problems with background process issues on apache/php.  It also

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);
}
up
2
Numabyte
3 years ago
An important thing to consider is in some cases, like on MacOS, using "2>&1" to properly output string from exec() works well when you wrap the command in parenthesis.

For example:
WITH PARENTHESIS AROUND CMD
$cmd = "(mysqldump -h l ocalhost -u root -ppassword --databases login_practice_db > /Users/username/Desktop/testDB.sql) 2>&1";

$sys = shell_exec($cmd ); //Works and produces an error (localhost is poorly spaced on purpose)

WITHOUT PARENTHESIS AROUND CMD
$cmd = "mysqldump -h l ocalhost -u root -ppassword --databases login_practice_db > /Users/username/Desktop/testDB.sql 2>&1";

$sys = shell_exec($cmd ); //Does nothing and goes blank as if the result were error free. Empty string.

It's interesting how shell command execution can get down to how nuanced one might need to be with string parsing.
up
1
Which shell?
2 years ago
Does anything, anywhere mention WHICH SHELL shell_exec() runs?...on my system, it's running sh, instead of bash, which causes <() Process Substitution to fail with no error msg.

Shouldn't the docs mention which shell this function (& backticks ``) runs?
up
6
eric dot peyremorte at iut-valence dot fr
17 years ago
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.
up
6
joelhy
15 years ago
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');
?>
up
3
RoBorg
17 years ago
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()
up
5
kamermans at teratechnologies dot net
17 years ago
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.
up
2
tonysb at gmx dot net
22 years ago
shell_exec is extremely useful as a substitute for the virtual() function where unavailable (Microsoft IIS for example). All you have to do is remove the content type string sent in the header:

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

This works fine for me as a substitute for SSI or the virtual() func.

Anton Babadjanov
http://www.vbcn.com.ar
up
2
jack dot harris-7ot2j4ip at yopmail dot Com
10 years ago
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.
up
2
saivert at saivert dot com
16 years ago
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.
up
1
pedroxam at gmail dot com
5 years ago
Here is my gist to all:

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;
}
up
3
codeslinger at compsalot dot com
14 years ago
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.
up
1
mamedul.github.io
1 year ago
Cross function solutions for execute command using 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(); //Use this instead of 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";
    }   

}
up
2
rustleb at hotmail dot com
18 years ago
For capturing stdout and stderr, when you don't care about the intermediate files, I've had better results with . . .
<?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]);    //Don't really want to give any input

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

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

This isn't much different than a redirection, except it takes care of the temp files for you (you may need to change the directory from ".") and it blocks automatically due to the proc_close call.  This mimics the shell_exec behavior, plus gets you stderr.
up
0
shell master
1 year ago
You can use a one liner to print the output of a shell script like so:

<?= shell_exec("/proof.sh"); ?>
up
2
Anonymous
19 years ago
Be careful as to how you elevate privileges to your php script.  It's a good idea to use caution and planing.  It is easy to open up huge security holes.  Here are a couple of helpful hints I've gathered from experimentation and Unix documentation.

Things to think about:

1. If you are running php as an Apache module in Unix then every system command you run is run as user apache.  This just makes sense.. Unix won't allow privileges to be elevated in this manner.  If you need to run a system command with elevated privileges think through the problem carefully!

2. You are absolutely insane if you decide to run apache as root.  You may as well kick yourself in the face.  There is always a better way to do it.

3. If you decide to use a SUID it is best not to SUID a script.  SUID is disabled for scripts on many flavors of Unix.  SUID scripts open up security holes, so you don't always want to go this route even if it is an option.  Write a simple binary and elevate the privileges of the binary as a SUID.  In my own opinion it is a horrible idea to pass a system command through a SUID-- ie have the SUID accept the name of a command as a parameter.  You may as well run Apache as root!
up
1
wally at soggysoftware dot co dot uk
12 years ago
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.
up
3
jessop <AT> bigfoot <DOT> com
20 years ago
Just a quick reminder for those trying to use shell_exec on a unix-type platform and can't seem to get it to work. PHP executes as the web user on the system (generally www for Apache), so you need to make sure that the web user has rights to whatever files or directories that you are trying to use in the shell_exec command. Other wise, it won't appear to be doing anything.
up
0
martin at intelli-gens dot com
14 years ago
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
up
0
gabekon at gmail dot com
16 years ago
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
up
0
mcbeth at broggs dot org
21 years ago
As far as error checking on the last example.  Several of the shells have the && operator, so you just string your commands together using it instead of ; If at any time any of the programs fail, you will return without running the rest
up
-1
dburles@NOSPAMgmailDOTcom
15 years ago
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
up
-2
Ashraf Kaabi
18 years ago
I've write a Full Class for Run in Background, Kill PID , check if is Running

<?php
/**
* @author     Ashraf M Kaabi
* @name       Advance Linux Exec
*/
class exec {
   
/**
     * Run Application in background
     *
     * @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);
   }
  
/**
    * Check if the Application running !
    *
    * @param     unknown_type $PID
    * @return     boolen
    */
  
function is_running($PID){
      
exec("ps $PID", $ProcessState);
       return(
count($ProcessState) >= 2);
   }
  
/**
    * Kill Application PID
    *
    * @param  unknown_type $PID
    * @return boolen
    */
  
function kill($PID){
       if(
exec::is_running($PID)){
          
exec("kill -KILL $PID");
           return
true;
       }else return
false;
   }
};
?>
up
-1
Martin Rampersad
20 years ago
I have PHP (CGI) and Apache. I also shell_exec() shell scripts which use PHP CLI. This combination destroys the string value returned from the call. I get binary garbage.  Shell scripts that start with #!/usr/bin/bash return their output properly.

A solution is to force a clean environment.  PHP CLI no longer had the CGI environment variables to choke on.

<?php

// Binary garbage.
$ExhibitA = shell_exec('/home/www/myscript');

// Perfect.
$ExhibitB = shell_exec('env -i /home/www/myscript');

?>

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

echo("Output.\n");

?>
-- end /home/www/myscript
up
-2
Paul Cook
18 years ago
The technique mentioned by Nathan De Hert below is rather insecure -- you should never leave a password lying around in a file readable by the apache user.

If you need this sort of functionality on *nix systems, have a look at the /etc/sudo file (edited with the command 'visudo'). The tag NOPASSWD allows specified commands to be run as root by another user, without needing to specify a password. It's a little extra configuration, but much more secure.
up
-1
smithnigelw at gmail dot com
6 years ago
With PHP on Windows, if you get the 'Warning: shell_exec() [function.shell-exec]: Unable to execute' error, then you need to check the permissions on file 'C:\WINDOWS\system32\cmd.exe'. You need read/execute permission on this file.  I would recommend using the sysinternals Process Monitor 'procmon.exe' to confirm the user that is trying to run 'cmd.exe'. Filter on 'Process Name' is 'php-cgi.exe' and 'Path' ends with 'cmd.exe'. Look at the event properties for the task with the access denied error, and it will show you the 'Impersonating' user name.  This is usually the 'Internet Guest Account', often 'NT AUTHORITY\IUSR'.
up
-1
phillipberry at NOSPAM dot blisswebhosting dot com
19 years ago
I found something odd.

If you run exec then straight after shell_exec the shell_exec will simply not run and will return NULL.

To get it to work i put a sleep(5) after the exec and now shell_exec works fine.
up
-1
ruan at nospam dot tillcor dot com
18 years ago
When following Kenneth's method for executing root scripts via the nanoweb server mentioned on this page you would most likely need to be able to run a text-mode browser like lynx and pass the php script to it (works great).

After struggling for a while (lynx kept asking me to download the file instead of executing it), I realised that I had to install php-cgi additionally and modify the nanoweb config file  to use that php interpreter instead of /usr/bin/php. (On Debian this is the CLI version).

On Ubuntu 6.06:

apt-get install php5-cgi

After editing /etc/nanoweb/nanoweb.conf and a quick restart of the web server, lynx and links will execute your PHP scripts properly.

Hope this helps somebody else out there :)

Ruan Fourie
up
-2
James McCormack
20 years ago
I had a perl program which ran fine from command line but not using shell_exec(), exec() or system() - nothing was being returned. I was using the full path and permissions were set correctly.

It turned out the perl program was using more memory than my PHP.INI file was set to allow. Increasing "memory_limit" solved the problem.
up
-2
concept at conceptonline dot hu
19 years ago
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.
up
-3
php [AT] jsomers [DOT] be
19 years ago
<?php

/**
* PHP Kill Process
*
* Sometimes, it can happen a script keeps running when it shouldn't, and it
* won't stop after we close the browser, or shutdown the computer. Because it's
* not always easy to use SSH there's a workaround.
*
* @author      Jensen Somers <php@jsomers.be>
* @version     1.0
*/

class KillAllProcesses {
   
/**
     * Construct the class
     */
   
function killallprocesses() {
       
$this->listItems();
    }
   
   
/**
     * List all the items
     */
   
function listItems() {
       
/*
         * PS   Unix command to report process status
         * -x   Select processes without controlling ttys
         *
         * Output will look like:
         *      16479 pts/13   S      0:00 -bash
         *      21944 pts/13   R      0:00 ps -x
         *
         */
       
$output =   shell_exec('ps -x');
       
       
$this->output($output);
       
       
// Put each individual line into an array
       
$array  =   explode("\n", $output);
       
       
$this->doKill($array);
    }
   
   
/**
     * Print the process list
     * @param   string  $output
     */
   
function output($output) {
        print  
"<pre>".$output."</pre>";
    }
   
   
/**
     * Kill all the processes
     * It should be possible to filter in this, but I won't do it now.
     * @param   array   $array
     */
   
function doKill($array) {
       
/*
         * Because the first line of our $output will look like
         *        PID TTY      STAT   TIME COMMAND
         * we'll skip this one.
         */
       
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.
up
-2
rigsbr at yahoo dot com dot br
18 years ago
If you need to execute a command without permission and could not execute it by ssh or install any extension, there is a way in Apache 1.3.x and PHP 4.
Create a file on cgi-bin directory, like this:

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

Don't forget to set the file you created the permission to execute it. Hence, call it from browser and you will se that this script will be executed by the shell user and not the user nobody (apache default user if running a PHP script).
up
-12
ricky at rocker dot com
6 years ago
despite:

shell_exec("env")

returning

SHELL=/bin/bash

I simply could not get:

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

to work. I have now got it working by forcing the bash shell using:

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