User

You are logged in as Anonymous.

Want to log out?

My friend Paul has a cool service called Wonderproxy that lets you test and develop GeoIP-based apps without the normal headaches. If you need to simulate remote, international traffic, you should check it out.

PHP Pie?

I've often had to manipulate large blobs of text—no, make that many files containing large blobs of text.

Of course, my IDE can usually handle simple search-and-replace operations, I appreciate the simplicity of the command line interface, on most occasions.

That's one of the reasons I love working in a unixy environment, I think. There's a bunch of utilities that embrace the command line and take simple input and deliver equally simple output. I've employed sed and awk, in the past, and I still use them to perform some very simple parsing. For example, I can often be found doing something like ps auxwww | grep ssh | awk {'print $2'} to get a list of ssh process IDs, for example.

But almost anyone who's ever been enlightened to perl pie delights in its power. In a nutshell, I can do something like perl -p -i -e 's/foo/oof/g' somefile from the command line, and perl will digest every line of somefile and perform the substitution. Perl is very well suited to this type of operation, what with its contextual variables and all.

I updated the code a little, below. You now must explicitly set $_.

Read on for my PHP-based solution (lest planet-php truncate my post). I've often found myself looking for a PHP equivalent. Not to do simple substitutions, of course, but complex ones. And since I'm most comfortable with PHP, and a I have a huge library of snippets that I can dig out to quell a problem that I may have solved years ago, I've been meaning to fill this void for a while.

Tonight, I had to come home from a dinner party, early, because my daughter was sick. Too bad, it looked like it was going to be an amazing feast, but I digress. The home-on-a-Saturday-night time left me with a bit of free time to solve one of the problems that's been floating around in my head for who-knows-how-long.

Thus, I'm happy to present my—at least mostly—working PHP pie script.

#!/usr/bin/php
<?php
 
// Change the shebang line above to point at your actual PHP interpreter
 
$interpreter = array_shift($_SERVER['argv']);
$script = array_shift($_SERVER['argv']);
$files = array_filter($_SERVER['argv']);
 
if (!$script) {
	fwrite(STDERR, "Usage: $interpreter <script> [files]\n");
	fwrite(STDERR, "  Iterates script over every line of every file.\n");
	fwrite(STDERR, "  \$_ contains data from the current line.\n");
	fwrite(STDERR, "  If files are not provided, STDIN/STDOUT will be used.\n");
	fwrite(STDERR, "\n");
	fwrite(STDERR, "  Example: ./pie.php '$_ = preg_replace(\"/foo/\",\"oof\",\$_);' testfile\n");
	fwrite(STDERR, "    Replaces every instance of 'foo' with 'oof' in testfile\n");
	fwrite(STDERR, "\n");
	exit(1);
}
 
// set up function
$func = create_function('$_', $script .';return $_;');
 
if (!$files) {
	// no files, use STDIN
	$buf = '';
	while (!feof(STDIN)) {
		$buf .= $func(fgets(STDIN));
	}
	echo $buf;
} else {
	foreach ($files as $f) {
 
		if (!is_dir($f) or !is_writable($f)) {
			fwrite(STDERR, "Can't write to $f (or it's not a file)\n");
			continue;
		}
 
		$buf = '';
		foreach (file($f) as $l) {
			$buf .= $func($l);
		}
		file_put_contents($f, $buf);
	}
}
 
?>

Hope it helps someone out there.

Update: I've had some people ask me why I'm reinventing the wheel. I did cover this above—I have plenty of existing PHP code snippets, and almost no perl. I also am very comfortable in PHP, but it's been years since I've been comfortable in perl.

Here's an example of something I hacked up, today. I can (relatively) easily turn this:

dmesg | tail -n5

... which returns this:

[17214721.004000] sdc: assuming drive cache: write through
[17214721.004000]  sdc: sdc1
[17214721.024000] sd 7:0:0:0: Attached scsi disk sdc
[17214721.024000] sd 7:0:0:0: Attached scsi generic sg1 type 0
[17214722.464000] FAT: utf8 is not a recommended IO charset for FAT filesystems, filesystem will be case sensitive!

(the first field is the time since boot... useless for my feeble human brain)

into:

dmesg | ./pie.php 'static $prev = false; static $boot = false; if (!$boot) {
list($boot) = explode(" ", file_get_contents("/proc/uptime"));
$boot = time() - (int) $boot;} if (!$_) return; list($ts, $log) = explode(" ", $_, 2);
$ts = str_replace(array("[","]"), array("",""), $ts); $_ = date("H:i:s", $boot + $ts);
if ($prev && ($diff = round($boot + $ts - $prev, 2))) $_ .= " (+". $diff .")"; 
$_ .= " ".$log; $prev = $boot + $ts;' | tail -n 5

(line breaks added for easier reading)... which returns:

17:07:44 sdc: assuming drive cache: write through
17:07:44  sdc: sdc1
17:07:44 (+0.02) sd 7:0:0:0: Attached scsi disk sdc
17:07:44 sd 7:0:0:0: Attached scsi generic sg1 type 0
17:07:45 (+1.44) FAT: utf8 is not a recommended IO charset for FAT filesystems, filesystem will be case sensitive!

That's the sort of thing I wouldn't be comfortable doing in perl, but I hacked up on the command line in PHP.


8 Responses to PHP Pie?

  1. 231 Ilia Alshanetsky 2007-02-18 16:38

    Why re-invent the wheel? ;-)

  2. 232 Yann 2007-02-18 19:17

    Humm interesting, however a simple editor like VIM does this very nicely and you can even specify a line range :)

    Here is how to do it in vim

    :10,50s/search/replace/g

    This will replace all the stuff from line 10 to 50 with the keyword asked. It could also be handy to reaplce in all open buffers (files) but i got too lazy to dig into. I keep this question with his Vim Presntation :)

    Cheers

  3. 233 Jan Schneider 2007-02-19 03:49

    @Yann: But vim doesn't allow this from the command line, or am I missing something?
    @Ilia: The subtle but important difference to perl -p -i -e is that you can run [i]any[/i] PHP expression on each line of the processed file. Most processing could be done with Perl too of course, but some native PHP functions could become very handy.

  4. 234 Yann 2007-02-19 13:38

    Yep it works with vim in command line ( i never tought doing this before)

    In command line you just have to do this.
    vim -c :10,50s/test/work/g test.txt -c :wq

    I guess a php script could be usefull too.

  5. 235 anonymous 2007-02-20 09:05

    > a simple editor like VIM

    erm, if Vim is 'simple' then what is complex ?

    anyway :

    - Vim does not run on windows (well, with cygwin it could)...

    - PHP does (without cygwin)

    and :

    - Vim is as verbose and user-friendly as an old A4 scansheet of sandy newspaper used for toilet-paper replacement.

    - PHP is as verbose and user-friendly as VIM, but still more user-friendly than perl and its s/wtf/omg/g hideous syntax.

    so :

    - Your comment assumes everyone is using gnu linux, and of course Vim as the preferred text editor

    - Reality is there are MacOS, Windows, and of course tons of editors (including EMACS) that can perform that job too.

    hint :

    do not fear php

  6. 236 Sean Coates 2007-02-20 20:20

    FYI, entry updated.

    S

  7. 237 Tobias Struckmeier 2007-02-22 11:06

    Yes VIM is simple.

    There is GVIM for windows. Even in a "simple" mode for people that are not yet used to the VIM commands.

    What verbosity do you need? Vim is able to mark the replacements, ask for every single and so on...

    And on windows I also prefer VIM (GVIM).

    MacOS I think textmate does the job (Compare it with vim and you find many functions that are very closely)

    You should try using vim for a while. Then youll be able to harnest its powers.

  8. 12067 anonymous 2009-04-01 20:06

    It is impossible to do substitutions using (G)VIM on a file, say 1GB big....and thus perl -p-i-e comes to the rescue :)

Leave a Reply



Clicky Web Analytics