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?
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?
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.
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.
(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.
find . -name "*.php" -print | xargs sed -i 's/foo/bar/g'
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.
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
perl -ibak -p -e 's/patterntomatch/replacem enttext/;' 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.*continuati onrequired string.*an otherrequi redparamet er
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.
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.*continuati
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.
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.
Be aware that usually GNU sed has a limitation of 4000 bytes for pattern length.
I use the following script to replace string in directories/subdirectories /file-name s/content
#!/bin/bash
ROOT_DIR="/target/director y" # your target dir
FILTER_FILE="/directory/fi lter.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;
#!/bin/bash
ROOT_DIR="/target/director
FILTER_FILE="/directory/fi
# 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;