How to find and replace files contents that match a pattern?

Suppose we have a folder named Project. I would like find each file in the folder that contains the pattern &$ at the function call. For example should replace: AnyFunction(&$arg);


with

AnyFunction($arg);  Where AnyFunction is an unknown function that matches the pattern. I need this to try to solve this php problem. If possible I would like too, if a function matchs the pattern, its declaration changes too: function AnyFunction(&$arg)


How can I perform this?

-
Am I going blind, or are your search and replace examples already identical? –  bahamat Aug 31 '12 at 21:46
Now is right^^ hihu –  GarouDan Aug 31 '12 at 23:45

2 Answers

A simple text approach where you replace AnyFunction(arg1, &$arg2) by AnyFunction(arg1,$arg2) unless preceded by the word function, will work if your source code is reasonably formatted: no function declaration on the same line as a function call, no comments or newline between function and the function name, no comments containing unbalanced parentheses inside function calls, etc. This will catch comments and string literals and may miss some complicated function calls (e.g. it doesn't catch &$arg2 in f(g(), &$arg2)).

Run this script on your files, they will be modified in place. Warning: untested, make backups first!

#!/usr/bin/perl -pi
BEGIN { use warnings; use strict; undef $/; } sub straighten_ampersands { my ($args, $want_ampersands) = @_;$want_ampersands = ($want_ampersands ? "&" : "");$args =~ s{ ([(,]\s*) &? (\$\w) } {$1 $want_ampersands$2 }x;
}
s{
(\bfunction\s+|)(\w+\s+)      # optional 'function' keyword, then function name
($$(?:(?>[^()]+)|(?4))*)$$))  # balanced parentheses (from perlre)
}{
"$1$2" . straighten_ampersands($3,$1 ne "")
}ex

-

This is a fairly straight-forward search and replace with sed.

1. the simplest case, if you can be fairly certain that there aren't any other instances of &$ that you don't want changed: find Project -name '*.php' -print0 | xargs -0r sed -i -e 's/&\$/$/g' 2. As above, but restricting the search-and-replace to just the first arg of a function call or definition (as in your example above): find Project -name '*.php' -print0 | xargs -0r sed -i -e 's/(&\$/($/g' 3. As in 1. above but restricting the replace to lines where &$ is found inside parentheses:

find Project -name '*.php' -print0 | xargs -0r sed -i -e '/([^)]*&\$[^)]*)/ s/&\$/$/g' note1: this will still change any other &$ on the same line that are outside parentheses, but only on lines where it has already been found inside parentheses (...&$...) note2: this will not change &$ on the second and subsequent lines of multi-line function calls or definitions....they don't match the search criteria.

In all cases, make a working copy of your Project directory and work with that. When it's changed to what you want, and you've tested that it works (at least without introducing more problems than it solves), move the real Project directory aside (e.g. mv Project Project.old) and then move the modified Project into place (e.g. mv Project.workingcopy Project)

-