Find all files with text "example.html" and replace with "example.php" works only if no spaces are in file name Find all files with text "example.html" and replace with "example.php" works only if no spaces are in file name unix unix

Find all files with text "example.html" and replace with "example.php" works only if no spaces are in file name


find public_html/ -type f -exec grep -q "$oldstring" {} \; -print0 |    xargs -0 sed -i '' s@"$oldstring"@"$newstring"@g

find will print all the filenames for which the grep command is successful. I use the -print0 option to print them with the NUL character as the delimiter. This goes with the -0 option to xargs, which treats NUL as the argument delimiter on its input, rather than breaking the input at whitespace.

Actually, you don't even need grep and xargs, just run sed from find:

find public_html/ -type f -exec sed -i '' s@"$oldstring"@"$newstring"@g {} +


Here's a lazy approach:

grep -rl $oldstring public_html/ | xargs -d'\n' sed -i "s@$oldstring@$newstring@g"

By default, xargs uses whitespace as the delimiter of arguments coming from the input. So for example if you have two files, a b and c, then it will execute the command:

sed -i 's/.../.../' a b c

By telling xargs explicitly to use newline as the delimiter with -d '\n' it will correctly handle a b as a single argument and quote it when running the command:

sed -i 's/.../.../' 'a b' c

I called a lazy approach, because as @Barmar pointed out, this won't work if your files have newline characters in their names. If you need to take care of such cases, then use @Barmar's method with find ... -print0 and xargs -0 ...

PS: I also changed s@"$oldstring"@"$newstring"@g to "s@$oldstring@$newstring@g", which is equivalent, but more readable.