Like many people when I began using PHP I thought "This would kick butt for shell scripting!". So I thought I would write up a little HOWTO on migrating from PERL, BASH, etc. scripting to using PHP.

There are a few reasons one might want to migrate to PHP for shell scripting. Here are a few that I have come up with:

1.) Database conectivity. 2.) Easy integration into existing PHP website. 3.) Fast development time (I spent more time looking up BASH commands than writing it) 4.) The power of PHP in your shell!

o start off with you are going to want to make sure that you have a compiled binary of PHP somewhere in your $PATH (ie /usr/local/bin or /usr/bin). To compile a binary of (also known as CGI) version of PHP just type "./configure --with-[your DB here] --with-xml && make" and you should have a php binary sitting in your php source directory in a few minutes. Copy that binary into one of the paths above - for this HOWTO we will assume that it is in /usr/local/bin.

Now to make a generic easy to follow PHP shell script (foo.php):

#!/usr/local/bin/php -q
<?

    $var = 'foo';
    echo $var."\n";

?>
The line #!/usr/local/bin/php -q tells your *NIX what program to call when the script is make executable. So how do you make it executable? "chmod +x foo.php" will do the trick.

One common problem for any uber geek is a HUGE inbox. No matter how much tweaking I do to my .procmailrc I have at LEAST 1000 messages in my mailbox at any time. Wouldn't it be GREAT if you could copy your ~/Mail/* directories into a backup folder once a week and then tar those up once a month into an archive? Normally you could do this in BASH - but PHP lets you do it quickly and effectively. (NOTE: I use mutt and my directories are in actuality one file that mutt parses - replace ~/Mail with hatever mailbox you use. Pine uses one file per directory also.) Here is a simple script that copies the mail directories I want to save over to a holding tank once a week (Time tracking will be done via the crontab). Open up a file called "mail_backups.php" and copy this code into it.

#!/usr/local/bin/php -q
<?
// On large scripts you want to set timeout to
// 0
set_time_limit(0);

// TEMPBOX is where we store the backups
$TEMPBOX = '/home/jstump/Mail/backups';
// MAILBOX is the place where the current boxes are
$MAILBOX = '/home/jstump/Mail';

$boxes = array('inbox','php-list','outbox');
// Today's date - so we know when it was backed up.
$date = date("Y-m-d");
for($i = 0 ; $i < sizeof($boxes) ; ++$i){
    $current_box = $boxes[$i];
    $cp = `mv ${MAILBOX}/${current_box} ${TEMPBOX}/${current_box}-${date}`;
}

?>

That's it! (Remmber to chmod +x it) A few things to take note of are `'s which work like PERL or BASH, in that they let you execute shell commands - the resulting output will be in the variable $cp. Now we could make a seperate script to be used on a monthly basis that would tar up files from a given month - but why? We can expand this script to do more than one thing using a few variables that often get overlooked in PHP - $argc and $argv. Yep, that's right - PHP has C like arg variables. $argc is the number of arguments and $argv is a list of the arguments ($argv[0] is the filename - just like C).

#!/usr/local/bin/php -q
<?
set_time_limit(0);

// TEMPBOX is where we store the backups
$TEMPBOX = '/home/jstump/Mail/backups';
// MAILBOX is the place where the current boxes are
$MAILBOX = '/home/jstump/Mail';
// ARCHIVEDIR is the directory where you want
// to store the archives.
$ARCHIVEDIR = '/home/jstump/Mail/archives';

$boxes = array('inbox','php-list','outbox');
// Today's date - so we know when it was backed up.
$date = date("Y-m-d");

switch($argv[1]){
    case 1:
        for($i = 0 ; $i < sizeof($boxes) ; ++$i){
            $current_box = $boxes[$i];
            $cp = `mv ${MAILBOX}/${current_box} ${TEMPBOX}/${current_box}-${date}`;
        }
        break;
    case 2:
        for($i = 0 ; $i < sizeof($boxes) ; ++$i){
            $current_box = $boxes[$i];
            $cp = `tar czvfm archive-${date}.tar.gz ${TEMPBOX}/*`;
            $mv = `mv archive-${date}.tar.gz ${ARCHIVEDIR}`;
        }
        $rm = `rm -fr ${TEMPBOX}/*`;
        break;
    default:
        echo 'NOT ENOUGH ARGUMENTS!!!!!!!!!!'."\n";
        break;
}

?>

ow in your crontab you can call "~/bin/mail_backups.php 1" or "~/bin/mail_backups.php 2" and have it do one of two things. You can then put the corresponding scripts into your crontab (via crontab -e) to put these into motion.

Another GREAT example of how powerful PHP can be as a scripting language is by piping output from various programs (grep, sed & awk, etc.) into your script. WHOA! How do you do that, you say? Well actually it's pretty simple. Open up a file and call it output.php and copy the following code into it.

#!/usr/local/bin/php -q
<?

// open up STDIN
$fp = fopen('/dev/stdin','r+');

$n = 1;
while(!feof($fp)){
    // read our line in from STDIN
    $line = trim(fgets($fp,4096));
    echo $n.' ==> '.$line."\n";
    flush();
    ++$n;
}
echo 'DONE!'."\n";

fclose($fp);

?>

A few things to notice include the fact that PHP opens up /dev/stdin, which is the *NIX standard for standard input (aka |), the $line = trim(fgets($fp,4096)); which reads in a line up to \n or 4096 characters - trim() is used to get rid of trailing spaces, and flush() which flushes out the current buffer into the screen - on large scripts this is a must. Now "chmod +x output.php" and get ready to pipe some output to it. I recenly used this for some HUGE log processing - so it only makes sense to grab a log and really test this out! Grap your /var/log/messages and do the following:

cat /var/log/messages | ./output.php

NOTE: You MUST be root to do this cat /var/log/messages. You should have gotten a bunch of lines that looks somethine like this:

1 ==> Sep 10 04:02:01 professorx syslogd 1.3-3: restart.

Now in the while loop you could have exploded that string and examined parts of it. In your Apache logs you could have snatched 404 requests, etc. Of course you could do this with a regular open on the file, but having it open /dev/stdin makes the code more portable. For instance Apache logs can be located ANYWERE on a persons site and even then the format may be different. Lets say that the user agent (the persons browser used to make the request) is the 3rd item in the log line on your server but on your box at home its the 6th item in the log line - what then? Easy you pipe the output of an awk line into your PHP script like this:

For at work: cat /www/logs/access_log | awk '{print $3}' | ./output.php For at home: cat /www/logs/access_log | awk '{print $6}' | ./output.php

And you don't have to change the script at all! Since I learned this dirty little trick I have used it on our site all over the place - mostly for the above purpose. I need to process a log or something of that sort on one box and do the same on another, but they are in differing formats. The above line saves me the hassle of changing any code in output.php

*NIX's are extremely powerful in the fact that they give you almost infinate flexibility in what you an do with them. This was a basic introduction in throwing PHP into the mix of your UNIX scripting experience. So often I hear people telling me how PERL or BASH just can't do some of the things they need it to do. For instance what if you wanted to process the above logs on a bunch of different logs and you needed to not only get that data but easily put it into a MySQL table? You know you can't do that in BASH and doing it in PERL could be more hastle than it's worth (not to metion if you EVEN know PERL!). In our above PHP script all you would have to do is analyze $line in the while() loop and then do a mysql_query(); to insert the desired data into your MySQL tables. Furthermore, you could add a few arguments to tell it what you want it to process (ie 1 to grab browsers, 2 to get 404's, etc.)

This has been a pretty simple introduction to shell scripting in PHP. I hope you realized how much power you are underutilizing in PHP.

About the Author:

Joe Stump works for a large Media Metrix Top 500 Site and spends most of his day programming large scale PHP sites. You can often find him trolling #e on efnet or the Forums on PHPBuilder.com. He also posts a lot of code on his site http://www.miester.org