#native_company# #native_desc#
#native_cta#

Using PHP for Shell Scripting

By Joe Stump
on November 25, 2000

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