Improve company productivity with a Business Account.Sign Up

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 484
  • Last Modified:

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?
0
RKFcomputers
Asked:
RKFcomputers
  • 2
  • 2
  • 2
  • +2
1 Solution
 
eagerCommented:
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.
0
 
RKFcomputersAuthor Commented:
@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.
0
 
medveddCommented:
find . -name "*.php" -print | xargs sed -i 's/foo/bar/g'
0
A proven path to a career in data science

At Springboard, we know how to get you a job in data science. With Springboard’s Data Science Career Track, you’ll master data science  with a curriculum built by industry experts. You’ll work on real projects, and get 1-on-1 mentorship from a data scientist.

 
RKFcomputersAuthor Commented:
@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.
0
 
eagerCommented:
You can use a sed script.  You don't need to use xargs.  

find . -name \*.php -exec sed -f script.sed {} \;

Where script.sed contains
s/whatever/whichever/

You can use whatever delimiter you wish in place of '/' as long as it doesn't appear in your source.  You can also split your substitution into multiple patterns if that makes things easier.  Each command in the sed script is executed in order for each input line.  

Sed can be touchy at times.  Make a copy of a couple of the files and move them to a sandbox directory for testing.
0
 
arnoldCommented:
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.
0
 
medveddCommented:
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.
0
 
Fadi SODAH (aka madunix)Chief Information Security Officer, CISA, CISSP, CFR, ICATE, MCSE, CCNA, CCNP, CCIP, SCSC and SCECommented:
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;
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 2
  • 2
  • 2
  • +2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now