Kevin Florida
Kevin Florida

Reputation: 6771

Linux SED search replace multiple per line

I need to replace a whole bunch of PHP super globals in a clients website with a PHP function I made to clean the superglobals from xss attacks.

Here is what the original code might look like:

echo $_REQUEST['HELLO1'] . ' AND ' . $_REQUEST['HELLO2'];

I need it to look like this:

echo MYCLASS::myfunction($_REQUEST['HELLO1']) . ' AND ' . MYCLASS::myfunction($_REQUEST['HELLO2']);

The main issue, I need to do a search/replace on over 100 files! Yikes!

So my solution was this (in linux shell):

sudo sed -i 's/\$_REQUEST[.*\]/MYCLASS::myfunction(&)/g' *.php

This works great as-long-as only one instance of "$_REQUEST" occurs per line... However with multiple instances, it screws up and does this:

echo MYCLASS::myfunction($_REQUEST['HELLO1'] . ' AND ' . $_REQUEST['HELLO2']);

Upvotes: 3

Views: 3270

Answers (5)

wallyk
wallyk

Reputation: 57774

This should do it:

sed -i "s/\$_REQUEST\[\([^\x5d]*\)\]/MYCLASS::myfunction(\1)/g" *.php

I had a lot of trouble matching ], so I've punted with \x5d.

Upvotes: 0

anubhava
anubhava

Reputation: 785256

Try this sed command:

sed -i.bak 's/\$_REQUEST\[\([^]]*\)\]/MYCLASS::myfunction(\1)/g' *.php

or in perl:

perl -pe 's/\$_REQUEST\[([^]]*)\]/MYCLASS::myfunction(\1)/g' file.php

Upvotes: 1

John Kugelman
John Kugelman

Reputation: 361675

The problem is that .* is greedy and will find the longest possible match it can. To work around that use [^]]* instead so that you don't inadvertently grab up an extra set of square brackets.

sudo sed -i 's/\$_REQUEST\[[^]]*\]/MYCLASS::myfunction(&)/g' *.php

In other regex dialects you could also write .*? to make the wildcard non-greedy, but that doesn't appear to work in sed (at least not in my version, not even with sed -r).

Upvotes: 2

Explosion Pills
Explosion Pills

Reputation: 191749

sed 's/\$_REQUEST\[[^]]*\]/MYCLASS::myfunction(&)/g'

Upvotes: 1

Troy
Troy

Reputation: 11

In Perl, the following script will work where you pass the script the name of the file you are interested in
lets say the script is t.pl and your file is file.php
to output back to file.php
perl t.pl file.php > file.php
to output to another file so you don't overwrite your original
perl t.pl file.php > another_file.php

#!/usr/bin/perl

$FILE_NAME = $ARGV[0];

open (FILE_NAME) or die ("Could not open FILE_NAME information file: $FILE_NAME \n");
@file_contents = <FILE_NAME>;
close (FILE_NAME);


foreach $line (@file_contents) {
       chomp($line);
       $line =~ s/\$_REQUEST\[.*?\]/MYCLASS\:\:myfunction\($&\)/g;
       print $line." \n";
}

exit;

Upvotes: 1

Related Questions