ChipmunkNinja
Ninjas are deadly. Chipmunk Ninjas are just weird.
About this blog
Marc Travels
Marc on Twitter
JustLooking on Twitter

Marc Wandschneider is a professional software developer with well over fifteen years of industry experience (yes, he really is that old). He travels the globe working on interesting projects and gives talks at conferences and trade shows whenever possible.

My Publications:

My book, "Core Web Application Programming with PHP and MySQL" is now available everywhere, including Amazon.com

My "PHP and MySQL LiveLessons" DVD Series has just been published by Prentice-Hall, and can be purchased on Amazon, through Informit, or Safari


ABCHKMPRaRoSTVW
xxxxx-xxxxxxxxx
Jul 16, 2006 | 05:34:57
Program Execution in PHP: exec, system, passthru, and shell_exec, oh my!
By marcwan

PHP is a sufficiently rich programming environment that it is not common that I truly need to execute external programs on the server on which it executes. However, every once in a while, this situation does come along, and for these, it is important to understand the options that PHP provides, what their differences are, and their relative strengths and weaknesses.

There are four primary choices for executing external programs in PHP:

  • The system function.
  • The exec function.
  • The shell_exec function or its syntactic analogue, the backtick operator, ( ` ).
  • the passthru function.
There is, in fact, a fourth option in PHP 5, using a new set of functions all prefixed with proc_, such as proc_open and proc_close, but these are quite advanced, and beyond the scope of this article.

We will first discuss the usage of these three functions in their most basic forms.

The system() Function

The system function in PHP takes a string argument with the command to execute as well as any arguments you wish passed to that command. This function executes the specified command, and dumps any resulting text to the output stream (either the HTTP output in a web server situation, or the console if you are running PHP as a command line tool). The return of this function is the last line of output from the program, if it emits text output.

For example:


// Windows Users replace "ls ..." with "dir ..."
$lastLine = system('ls /Users/marcwan');
echo "<br/>LastLine: $lastLine<br/>\\n";

The output of this command on my system is:


Desktop Documents Download Library Movies Music Pictures
  Public Sites bin blah.php books make1.png make1.tiff media src
  yici1.png yici1.tiff
LastLine: yici1.tiff

The return value of the function will be FALSE if the function completely fails to execute.

In addition to the return value of the system function, however, there is also the question of the return status of the program being executed (just as functions in PHP all have return values (NULL if you don’t explicitly choose one), all commands executed in most operating systems also have return values, typically a 32bit integer value). For those programs you are executing whose return status you wish to know, you can pass a second argument to the system function, which tells PHP where to put this value.

For example:


// define this variable here so that it can actually be used in the system fn
$returnValue = -1;
system('ls /Users/marcwan', $returnValue);
echo "Return Value: $returnValue<br/>\\n";

The output of this program will be:


Desktop Documents Download Library Movies Music Pictures
  Public Sites bin blah.php books make1.png make1.tiff media src
  yici1.png yici1.tiff
Return Value: 0

For most modern operating systems, a return value of 0 is considered a success, and other values indicate failure. Some programs will have a range of values to indicate the exact way in which they failed while others will just return 1 or -1 to indicate that they were unsuccessful.

The exec() Function

The system function is quite useful and powerful, but one of the biggest problems with it is that all resulting text from the program goes directly to the output stream. There will be situations where you might like to format the resulting text and display it in some different way, or not display it at all.

For this, the exec function in PHP is perfectly adapted. Instead of automatically dumping all text generated by the program being executed to the output stream, it gives you the opportunity to put this text in an array returned in the second parameter to the function:


// you define this variable here so that it exists for the call to exec
$output = null;

// Windows users: 'dir c:\\' or something similar
exec('ls /Users/marcwan', $output);
echo "<pre>" . var_export($output, TRUE) . "</pre>\\n";

Once again, on my machine, the following output is printed:


array (
  0 => 'Desktop',
  1 => 'Documents',
  2 => 'Download',
  3 => 'Library',
  4 => 'Movies',
  5 => 'Music',
  6 => 'Pictures',
  7 => 'Public',
  8 => 'Sites',
  9 => 'bin',
  10 => 'blah.php',
  11 => 'books',
  12 => 'make1.png',
  13 => 'make1.tiff',
  14 => 'media',
  15 => 'src',
  16 => 'yici1.png',
  17 => 'yici1.tiff',
)

Of course, instead of just directly emitting this output, we could instead choose to process it and only emit those files with a ’.png’ extension, for example:


foreach ($output as $fileName)
{
  if (strtolower(substr($fileName, -4)) == '.png')
    echo "<br/>$fileName\\n";
}

And once again, you can get the return code from the exec function as follows:


$output = null;
$returnValue = -1;
exec('ls /Users/marcwan', $output, $returnValue);
echo "Return Value: $returnValue<br/>\\n";
echo "<pre>" . var_export($output, TRUE) . "</pre>\\n";

The shell_exec() Function

Most of the programs we have been executing thus far have been, more or less, real programs1. However, the environment in which Windows and Unix users operate is actually much richer than this. Windows users have the option of using the Windows Command Prompt program, cmd.exe (See Figure 1). This program is known as a command shell.

The Windows cmd.exe Command Shell

Figure 1— The Windows Command Prompt

Similarly, Unix (and Mac OS X) users can run a command shell such as sh, bash, or csh (see Figure 2). These are typically quite advanced interactive systems that allow elabourate scripts such as for x in *.png; do echo $x; done to be executed.

Apple OS X's command shell

Figure 2— bash on Mac OS X

On both Windows and Unix, the commands for the various command shells can be put into files and executed by these programs. On Windows, convention has it that these script file have the extension .cmd while on Unix it is not uncommon to see .sh.

For those sitautions where we want to run these shell scripts from within PHP, we can use the shell_exec function, as follows:



// lists all .bat files anywhere in the system
$output = shell_exec('FOR /R C:\\ IN (*.bat) DO echo %x');   // Windows

// lists all .png files in /Users/marcan only
$output = shell_exec('for x in /Users/marcwan/*.png; do echo $x; done'); // Unix

echo "<pre>" . var_export($output, TRUE) . "</pre>\\n";

On my system this produces:


'/Users/marcwan/make1.png
/Users/marcwan/yici1.png
'

PHP also has a syntactic operator called the backtick operator which does the exact same thing as the shell_exec function. Instead of calling the shell_exec function, you simply wrap the command in the backtick character, `, which is very different from the single quote character '.



// lists all .bat files anywhere in the system
$output = `FOR /R C:\\ IN (*.bat) DO echo %x`; // Windows

// lists all .png files in /Users/marcan only
$output = `for x in /Users/marcwan/*.png; do echo $x; done`; // Unix

echo "<pre>" . var_export($output, TRUE) . "</pre>\\n";

It is worth noting that I avoid this operator at all times as it is not easy to spot in code, and I like potential security trouble spots to be as visible as humanly possible.

The passthru() Function

One fascinating function that PHP provides similar to those we have seen so far is the passthru function. This function, like the others, executes the program you tell it to. However, it then proceeds to immediately send the raw output from this program to the output stream with which PHP is currently working (i.e. either HTTP in a web server scenario, or the shell in a command line version of PHP).

When would this be useful? When you are working with image files and want to execute a program to show or otherwise manipulate these files. A most simple example would be as follows:


/**
 * Windows:
 */
header('Content-Type: image/bmp');
passthru('type c:\\Windows\\zapotec.bmp');

/**
 * Unix
 */
header('Content-Type: image/jpg');
passthru('cat /Users/marcwan/yici1.png');

The result will be an image in your browser. This is extremely powerful if you want to use some programs to resize or otherwise manipulate image files and then just dump the output to the client. There are a number of interesting utilities that come with various JPEG source code distributions that do exactly this and are powerful tools.

Are We Done Yet?

Armed with this basic knowledge, we have a one major thing left to deal with before we go off into the real world and begin using these functions: Security.

Allowing users to specify any portion of the string you pass to the exec, system, shell_exec, or passthru functions is a huge security risk, and you need to plan carefully in advance before allowing any of this.

For example, the following code is simply a recipe for disaster:


$output = shell_exec('cat /webserver/info/user_files/' . $_POST['user_info_file']);
echo $output

If a malicious user were to set the value of the user_info_file post variable to bob; cat /etc/passwd. The output of both files would now be dumped, which is definitely not in your best interests.

There are two key ways which we will get around this:

  1. We will design our web applications carefully for security. If we can design the web application in such a way that only we choose the file names and programs that execution functions work with, we have much less to worry about in terms of security. We can also do our best to isolate any files that we work with and try to limit damage whenever something unexpected occurs.
  2. For those cases where we decide that user input is absolutely unavoidable, we will filter the user input extremely heavily and make sure it is as safe as possible before passing it to any of these functions. I always do the following:
    • Absolutely forbid the use of any slash characters, whether it be forward ( / ) or backward ( \ ). I also forbid any user strings from beginning with X:, where X could be a valid drive letter on the system (Windows only, of course).
    • Use the escapeshellarg function to further make the parameters safer. I.e. $output = shell_exec("cat /web/users/info/" . escapeshellarg($processedUserInfoFile));

A way in which we are helped by the operating system is that most web servers operate as a user with restricted permission, and thus can only do things that user is permitted to do.

One last security note we should mention here is that if your php.ini file has safe_mode set to On, then the shell_exec function and analagous backtick operator is not available.

Conclusion

We have covered a lot of ground in this article, but we should now have a solid understanding of the various execution functions available to us in PHP, as well as how they differ. While we should be extremely concerned about the security of running programs external to our web server, we can do so in a reasonably safe and controlled manner, which can only be a good thing for our web applications and their customers.

1 It is worth noting that dir, type, and many other commands on Windows are not programs, but actualy commands built into the cmd.exe program. Fortunately the system execution functions are smart enough to know to execute cmd.exe for you if you ask to execute one of them.

Run a command as any user throught the webserver
Posted By: John Feb 14, 2007 01:23:19
Here is something helpful maybe. If you want to run a command as a certain user root or whomever here is a function:

1. add the apache user to sudoers file:
www ALL = (ALL) PASSWD: ALL
2. give apache a password:
passwd www
3. use this function to execute command as the user

function execCmd($cmd, $user = 'root') {
$fhandle = popen("$this->SUDOCMD -v \n\n", "w");
fputs($fhandle, "mypassword");
fclose($fhandle);

$cmd = ereg_replace("\n\r","",$cmd);
$result = shell_exec("$this->SUDOCMD -u $user $cmd");
if (!is_null($result)) {
return ereg_replace("[\n \r]", "", $result);
} else {
return false;
}
}
shell_exec() not working on Windows
Posted By: Nick May 06, 2007 23:03:28
My shell_exec works just find... if I want to copy, move, or delete files. Oh, and I can change my directories just fine. But how to I initiate a program?

I am using PHP on a web server. It is my intention to allow user input to be processed, sent to a program, and processed by that program accordingly.

I am writing a web application which turns user input into an image using an image generator... but the generator is a separate, complex program, well beyond generic GDImage libraries.

I have no other way of embedding this programs functions into my web application except through command line prompts.
displaying image with passthru() doesn't work
Posted By: dave r. Aug 07, 2007 10:44:17
Ultra-simple code example:

<html>
<body>

<?php
header('Content-Type: image/jpg');
passthru('cat newsealb.jpg');
?>

</body>
</html>

This doesn't work. The browser error I get is:
The image “http://wwwtest.emailxl.com/~rain/passthru_image.php” cannot be displayed, because it contains errors.
I've tried it with various gifs, jpegs, and pngs
(with appropriate content-type tags). Always the
same error. All permissions and locations are
correct. I've also tried it on two different apache
servers, running PHP5 or above.

Code bug:
Posted By: marcwan Aug 08, 2007 06:18:09
Hey Dave.

This is a problem in your code.

you want to do the following:

--- BEGIN FILE blah.php----
<?php

header('Content-Type: image/jpeg');
passthru('cat newsealb.jpg');
?>
--- END FILE blah.php------

The problem is that you added regular HTML display headers, namely <html> and <body>. If you don't add those, however, you have the correct output that a web browser will understand.

marc.
for new line character's
Posted By: Anant Shrivastava Jun 23, 2008 08:12:46
for new line character's to be interpreted properly either replace then with <br /> or better to enclose the output in <pre></pre> tags.
Variable Expansion
Posted By: marcwan May 06, 2009 20:26:08
Hey Jacques:

variable expansion only works in double quote strings, not single quote strings:

$var = 10;

$str1 = "var is now: {$var}"; // $str1 is "var is now: 10"
$str2 = 'var is now: {$var}'; // $str2 is 'var is now: {$var}'


desperately need help with passthru
Posted By: Vincent Tsang Sep 12, 2009 09:23:03
my phpinfo can be found here:
http://swanson.jp/php/phpinfo.php
my php.ini has these lines :
safe_mode = Off
safe_mode_include_dir = ".;F:\HTDOCS\PHP;F:\apache\Apache\PHP.5.3.0\include"

my test html can be found here
http://swanson.jp/php/test6.html

http://swanson.jp/php/test6.php has the following lines

<html><body>
<?php

// passthru ################################
$ReturnValue = 9999;
passthru('dir > c:/abc.data', &$ReturnValue);
echo 'passthru $ReturnValue = ' + " " + $ReturnValue + "<br>";

// system ################################
$ReturnValue = 9999;
system("dir c:\\" , $ReturnValue);
echo 'system $ReturnValue = ' + " " + $ReturnValue + "<br>";

// exec ################################
// you define this variable here so that it exists for the call to exec
$output = null;

// Windows users: 'dir c:\\' or something similar
exec('dir c:\\', $output);
echo "<pre>" . var_export($output, TRUE) . "</pre>\\n";

foreach ($output as $fileName) {
echo "<br/>$fileName\n";
}

?>
</body></html>

Please help me what should I do to make it work.
I have spent weeks google trying to make it work.
I am very very desperate.

Thanks


desperately need help with passthru
Posted By: Vincent Tsang Sep 12, 2009 09:30:34
http://swanson.jp/php/test6.php has the following lines

I saved it as .txt so you can open & see it

http://swanson.jp/php/test6.txt


passthru
Posted By: marcwan Sep 13, 2009 04:59:35
You're using passthru incorrectly. It takes the output from the command and sends it directly to the output stream of the program (i.e. the webpage or stdout for command line PHP).

Also, it doesn't always execute the command in a shell, so to get the output you want, you might need to prefix the command with "cmd.exe" for windows and "sh" for Unix-like operating sytsems (note, i haven't tested the exact details in a few years).

mv $var
Posted By: marcwan Dec 20, 2009 05:52:08

barnamos,

the above code looks like it shouldn't work at all. I would expect PHP to give syntax errors all over the place with code like exec('mv' . $var /new/location);

If you did something like:

$var = 'file1.txt';

exec("mv $var /new/location");

it should work just fine.

it would be even better if you did:

exec("mv '$var' /new/location");

and you'd be even smarter to run shellescapecmd to really make sure things were safe.




good luck!
what can i do if the command prompts for more input?
Posted By: Daniel Jan 12, 2010 00:05:55
i have to write a vss integration in php, but this annoying ss.exe comes back with "are you sure?" or "do you also want to delete the copy?" etc. questions all the time. so i was wondering if it is possible to pass in more "input" into the shell once its running? Just as an example, if you try to execute "del c:\test.txt /p" it comes back with "Delete (Y/N)?" - is there a way to deal with that ans supply a "Y" input?

also, it might be a good idea to add " && exit" at the end of each command so php always closes the command line.
Great article
Posted By: hypothetical25 Jan 24, 2010 11:30:58
I'm just getting started with PHP and wanted to know what the different mechanisms of running serverside commands were and how they differed. Your article nailed it!

thanks a ton!
hypo
Add a Comment

Title:

Name:

URL:

Comment:

Copyright © 2005-2008 Marc Wandschneider All Rights Reserved.