Use command-line tools in PHP

Run shell commands in your Web applications

Learn how to better integrate scripts with command-line tools. Examine using shell_exec(), exec(), passthru(), and system(); safely passing information to the command line; and safely retrieving information from it.

Share:

Thomas Myer (tom@tripledogs.com ), Principal, Triple Dog Dare Media

authorThomas Myer is a consultant, author, and developer in Austin, Texas. Follow @myerman on Twitter to keep up with his doings.



19 May 2009

Also available in Japanese Portuguese

If you've been working with PHP, you know it's an excellent tool for creating feature-rich Web pages. As a general scripting language, PHP:

  • Is easy to learn.
  • Has many powerful frameworks, such as CakePHP and CodeIgniter, to make you as productive as any Rails programmer.
  • Can communicate with MySQL, PostgreSQL, Microsoft® SQL Server, and even Oracle.
  • Easily integrates with JavaScript frameworks, such as script.aculo.us and jQuery.

At some point, though, you want to do more, or you're forced to do more. By that, I mean you have to work directly with the file system of the server PHP is running on. You end up needing to work with files on the file system, understand what processes are running, or perform some other task.

At first, you're content using commands like file() in PHP to open files. At some point, though, the only way to get something done is to be able to run shell commands on the server and get back some type of output. For example, you might need to know how many files live in a certain directory. Or you may need to know how many lines have been written to a group of log files. Or you may need to operate on those files to copy them to another directory or use rsync to transport them to another location.

In "Command-line PHP? Yes, you can!," Roger McCoy shows how to use PHP directly from the command line — no Web browser needed. In this article, I approach the subject from the other point of view, showing you how to integrate closely with underlying shell commands and folding any return values into your interfaces and processes.

This kind of thing only works if you're running on Linux®, Berkeley Software Distribution (BSD), or some other flavor of UNIX®. I assume that you're running on a Linux-Apache-MySQL-PHP (LAMP) stack. Your mileage may vary if you're running a different UNIX flavor because availability of commands varies from installation to installation. I know many of you are also developing on Mac OS X, which runs a flavor of BSD, so I keep the sample commands as generic as possible to ensure portability.

Command-line overview

The PHP Command Line Interface (CLI) Server Application Programming Interface (SAPI) was released experimentally in PHP V4.2.0. As of V4.3.0, it is fully supported and enabled by default. The PHP CLI SAPI allows you to develop shell- and even desktop-based scripts powered with PHP. Indeed, it is possible to create tools in PHP that run directly from the command line. Working in this fashion, PHP developers can be as productive as Perl, AWK, Ruby, or shell scripters in that context.

This article considers the tools built into PHP that allow you to tap into the underlying shell environment and file system PHP is running on. PHP provides a number of functions for executing external commands, among them shell_exec(), exec(), passthru(), and system(). These commands are similar, but provide different interfaces to the external program you're running. Each of these commands spawns a child process to run the command or script you designate, and each captures the output of your command as it's written to standard output (stdout).

shell_exec()

The shell_exec() command is actually just an alias for the backtick (`) operator. If you've been doing any shell or Perl scripting at all, you know you can capture the output of other commands inside backtick operators. For example, Listing 1 shows how to use the backtick to obtain a word count for every text (.txt) file in the current directory.

Listing 1. Using backtick for word count
#! /bin/sh
number_of_words=`wc -w *.txt`
echo $number_of_words

#result would be something like:
#165 readme.txt 388 results.txt 588 summary.txt
#and so on....

In your PHP script, you can run that simple command inside shell_exec(), as shown in Listing 2, and get the results you need, assuming you have some text files in the same directory.

Listing 2. Running the same command in shell_exec()
<?php
$results = shell_exec('wc -w *.txt');
echo $results;
?>

As you can see in Figure 1, you get the same results as you would from the shell script. That's because shell_exec() lets you run an external program via the shell, then returns the result as a string.

Figure 1. Results of running a shell command through shell_exec()
A shell_exec() result example

Please note you get the same results if you just use the backtick operators, as shown below.

Listing 3. Using only the backtick operators
<?php
$results = `wc -w *.txt`;
echo $results;
?>

Listing 4 shows an even simpler method.

Listing 4. A simpler method
<?php
echo `wc -w *.txt`;
?>

It's important to note that pretty much whatever you can do on the UNIX command line or in a shell script is allowed here. For example, you can use pipes to string together commands. You can even create a shell script with all your operations in it and just call the shell script, with or without arguments, as needed.

For example, if you want to count only the words in the first five text files in the directory, you can use a pipe (|) to hook the wc and head commands together. Furthermore, you can wrap the output results in pre tags to make it more palatable in the Web browser, as shown below.

Listing 5. A more complex shell command
<?php
$results = shell_exec('wc -w *.txt | head -5');
echo "<pre>".$results . "</pre>";
?>

Figure 2 shows the results of running the script from Listing 5.

Figure 2. Results of running a more complex shell command through shell_exec()
Results of running a more complex shell command through shell_exec()

Later in this article, you'll learn how to pass arguments to these scripts using PHP. For now, you can think of it as a way to run shell commands, as long as you remember that you'll only see standard output. If there are errors in your script or command, you won't see a standard error (stderr) unless you pipe it to stdout.

passthru()

The passthru() command lets you run an external program and display its results on the screen. You don't need to use echo or return to see these results; they simply display in the browser. You can add an optional argument, a variable that holds the return code from the external program, such as 0 for success, which provides a better mechanism for debugging.

In Listing 6, I use the passthru() command to run the little word-count script I ran in the previous section. As you can see, I also add a $returnval variable that contains the return code.

Listing 6. Using the passthru() command to run word-count script
<?php
passthru('wc -w *.txt | head -5',$returnval);
echo "<hr/>".$returnval;
?>

Notice that I don't have to echo anything out. The results just end up on the screen, as shown below.

Figure 3. Results of running the passthru() command with a return code
passthru() command example

In Listing 7, I introduce a small error in the code by removing the dash before the 5 in the head portion of the script.

Listing 7. Introducing an error to the word-count script
<?php
//we introduce an error below (removing - from the head command)

passthru('wc -w *.txt | head 5',$returnval);
echo "<hr/>".$returnval;
?>

Notice that the script does not run as intended. As shown in Figure 4, you get a blank screen, a horizontal rule, and a return value of 1. This return code usually indicates some kind of error has occurred. Being able to test for this return code makes it easier to figure out what needs fixing.

Figure 4. Seeing the error code when using passthru()
passthru() error return code

exec()

The exec() command is similar to shell_exec(), except it returns the last line of the output and, optionally, populates an array with the full output of the command, along with the error code. Listing 8 is an example of what happens if you run exec() without capturing the results in a data array.

Listing 8. Running exec() without capturing the results in a data array
<?php
$results = exec('wc -w *.txt | head -5');
echo $results;

#would print out just the last line or results, i.e.:
#3847 myfile.txt
?>

To capture the results in an array, add the name of the array as a second argument to exec(). I do this in Listing 9, using $data as my array name.

Listing 9. Capturing the results from exec() in a data array
<?php
$results = exec('wc -w *.txt | head -5',$data);
print_r($data);

#would print out the data array:
#Array ( [0]=> 555 text1.txt [1] => 283 text2.txt) 
?>

After you capture the results in an array, you can do something with each line. For example, you can split on the first space you find and store the discreet values in a database table, or you can apply specific formatting or tags to each line.

system()

The system() command, shown in Listing 10, is something of a hybrid. Like passthru(), it outputs anything it receives directly from the external program. Like exec(), it also returns the last line and makes the return code available.

Listing 10. The system() command
<?php
system('wc -w *.txt | head -5');

#would print out:
#123 file1.txt 332 file2.txt 444 file3.txt
#and so on
?>

Some examples

Now that you've learned how to use all these PHP commands, you probably have questions. For instance, which commands should you use and when? This is entirely up to you and the needs you have.

Most of the time, I use the exec() command with the data array to do any processing. Otherwise, I use shell_exec() for simpler commands, especially if I don't care about the output. If I just need to run a shell script, I use passthru(). Often, I find myself using the functions for different reasons and sometimes interchangeably. It all depends on my mood and what I'm trying to accomplish.

Another question you might have is "What is all this stuff good for?" In case you're stuck for ideas or a project that just jumps out as a good way to use shell commands hasn't presented itself, I offer a few ideas here.

If you are writing an application that offers any kind of backup or file transfer capabilities, you'd be smart to use shell_exec() or one of the other commands here to run an rsync-powered shell script. You can write the shell script that contains the necessary rsync commands, then use passthru() to execute it based on a user command or a cron job.

For example, a user with the appropriate privileges in your application, such as admin, might need to transfer 50 PDFs from one server to another. The user would navigate to the correct place in your application, click Transfer, select the PDFs to transfer, then click Submit. As its action, the form would have a PHP script that runs your rsync script via passthru() with a return option variable so you know if a problem occurs, as shown below.

Listing 11. Sample PHP script that runs an rsync script via passthru()
<?php
passthru('xfer_rsync.sh',$returnvalue);

if ($returnvalue != 0){
    //we have a problem!
    //add error code here
}else{
    //we are okay
    //redirect to some other page
}
?>

If you have an application that needs to list processes or files, or some data about those processes or files, you can easily use any of the commands summarized in this article to do just that. For example, a simple grep command can help you find files that match certain search criteria. Using this in concert with the exec() command and dumping the results to an array allows you to build an HTML table or form that then allows you to run other commands.

So far, I've discussed user-generated events — if the user presses a button or clicks a link, PHP runs a script. You can also achieve some interesting effects by running stand-alone PHP scripts with cron or another scheduler. For example, if you have a backup script, you can run it stand-alone via cron, or you can wrap it in a PHP script and then run it. Why would you want to do that? It sounds redundant and wasteful, right? Well, no — not if you consider that you can run the backup script through exec() or passthru(), then perform some behavior based on the return code. If you get an error, you can write an entry in an error log or a database, or send a warning e-mail message. If the script succeeds, you can dump the raw output of the script to the database (for example, rsync has a verbose mode useful in diagnosing problems later on).


Security

One quick statement here about security: If you're accepting user input and passing that information along to the shell, you better sanitize that user input. Strip out any commands that you think might be harmful and disallow things, such as sudo (running with superuser privileges) or rm (delete). In fact, you might want to disallow users from sending in open requests and only allow them to choose from a list of possible alternatives.

For example, if you are running a transfer program that accepts a list of files as an argument, you might list all your files in a row with a checkbox next to each one. Users can select and deselect files and click Submit to activate the rsync shell script. They wouldn't be allowed to type in a list of files or use some kind of regular expression.


Summary

In this article, I showed the basics of using PHP commands like shell_exec(), exec(), passthru() and system() to run shell scripts and other commands. It's now up to you to put this knowledge to use in your own applications.

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Open source on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=389459
ArticleTitle=Use command-line tools in PHP
publish-date=05192009