Avatar of RKFcomputers
RKFcomputers
 asked on

Linux Command Line Search & Replace Script

I need some help writing a bash script (or something else, if suggested) that will scour recursively through all the php files in a folder and subfolders, and do a search & replace.

The text to match is quite long, so I'm guessing a script file is more appropriate than just a command line entry.

Any ideas?
LinuxOS SecurityPerl

Avatar of undefined
Last Comment
madunix

8/22/2022 - Mon
Michael Eager

Take a look at find and sed.  You can create a sed script to do the replacement and drive is using find to select the correct files.
RKFcomputers

ASKER
@eager, Thanks - I know enough to know that find & sed are the likely tools to use, but not enough about how to employ them properly in this case.

(Honestly, we're an SEO company trying to help a friend. I know just enough Linux to be dangerous, as they say.)

Hence my request for help here in crafting the right approach.
medvedd

find . -name "*.php" -print | xargs sed -i 's/foo/bar/g'
This is the best money I have ever spent. I cannot not tell you how many times these folks have saved my bacon. I learn so much from the contributors.
rwheeler23
RKFcomputers

ASKER
@medvedd, Thank you!

That's a great start ... but what's the best approach when "foo" is actually about 2,000+ characters?

I know how I'd handle it in PHP by putting the strings into variables ... but I'm not up enough on bash scripting to make sure I don't bugger it up.
ASKER CERTIFIED SOLUTION
Michael Eager

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
arnold

perl -ibak -p -e 's/patterntomatch/replacementtext/;' list_of_files
Matched files will be backed up with a .bak suffix.

Are there any other delineations I.e. the item being replaced is enclosed in comment markers //
foo nor pattern have to be the entire text being replaced, but has to be unique enough to match I.e.
Startingstring.*continuationrequiredstring.*anotherrequiredparameter
The problem is if the dt to match spans multiple lines rather being a auto wrapped single line.
In the multiline, additional options have to be used with sed or perl to look for foo to match across line.

In foo and pattern can be used as a variable
foo="pattern"
Etc.

Perl does the find and replace in a single line command.  It can be converted into a perl script with the while loop going line by line matching on the first occurrence of a defined pattern, then trying to compare the reminder to match the entire pattern.
Etc.
medvedd

As @eager mentioned, you can make sed script and put long patterns there.
Be aware that usually GNU sed has a limitation of 4000 bytes for pattern length.
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
madunix

I use the following script to replace string in directories/subdirectories/file-names/content

#!/bin/bash
ROOT_DIR="/target/directory"  # your target dir
FILTER_FILE="/directory/filter.sed"  # the sed script for renaming

# custom rename function that uses $FILTER_FILE (via sed)
function rename_using_filter {
    CURRENT_NAME="$1"
    NEW_NAME="$(echo $1 | sed -f $FILTER_FILE)"  # derive new name
    if [ "$CURRENT_NAME" != "$NEW_NAME" ]; then  # rename if diff
        mv "$CURRENT_NAME" "$NEW_NAME"
    fi
}

# for each directory, starting from deepest first
while IFS= read -r -d $'\0' DIR_NAME; do
    cd "$DIR_NAME"           # go to target dir

    # for each file/dir at this level
    while IFS= read -r -d $'\0' FILE_NAME; do
        if [ -f "$FILE_NAME" ]; then                # if it's a file
            sed -i -f "$FILTER_FILE" "$FILE_NAME"   # replace content
        fi
        rename_using_filter "$FILE_NAME"  # rename it
    done < <(find . -maxdepth 1 -print0)

    cd - > /dev/null         # back to original dir. Suppress stdout
done < <(find $ROOT_DIR -depth -type d -print0) # get only dirs




In the filter.sed put your strings
# cat filter.sed
s/old1/new1/g;
s/old2/new2/g;
s/old3/new3/g;

s/oldn-1/newn-1/g;
s/oldn/newn/g;