Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have used the following to do a recursive find and replace within files, to update hrefs to point to a new page correctly:

#!/bin/bash
oldstring='features.html'
newstring='features.php'
grep -rl $oldstring public_html/ | xargs sed -i s@"$oldstring"@"$newstring"@g

It worked, except for a few files that had spaces in the name.

This isn't an issue, as the files with spaces in their names are backups/duplicates I created while testing new things. But I'd like to understand how I could properly pass paths with spaces to the sed command, in this query. Would anybody know how this could be corrected in this "one liner"?

like image 333
Tiago Avatar asked Dec 31 '13 13:12

Tiago


2 Answers

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 {} +
like image 172
Barmar Avatar answered Nov 11 '22 18:11

Barmar


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.

like image 43
janos Avatar answered Nov 11 '22 18:11

janos