⚠️ This article was originally published in 2005 at dubi.org/nix-search-and-replace. The content is extremely outdated and is preserved here for nostalgic purposes only.
I couldn’t figure out how to do a search and replace in multiple files on the command line, so I sat down with someone who knows the CLI a lot better than I and we (he) came up with this:
find . -iname \*.php -or -iname \*.inc -or -iname \*.html -or -iname \*.htm | sed 's/ /\\ /g' | sed 's/#/\#/g' | xargs sed -i 's/text1/text2/gi'
This searches the contents of all .php, .inc, .htm, and .html files, recursively from the current directory, and replaces occurrences of text1 with text2.
This handles files with spaces and pound signs (#) in the name without breaking, the one requirement I had which I couldn’t find on the net.
Of course, if you’re only interested in the search part:
grep -r text *
Problem with that is binary files mucking up your searches. Just specify “—binary-files=without-match” as an option. Also, let’s say you only want to count the number of matches. Let’s throw in some case-insensitivity as well:
grep --binary-files=without-match -ir text * | wc -l
Another neat trick is to specify -v, which inverts matches (count the number of files that don’t contain “text” if we added it in the above example).
This can also be used to filter out something unwanted from search results. For example, to find all files that contain “text1” but don’t have “.svn” in their path:
grep -R text1 * | grep -v .svn
or to strip out comments from a C++ project:
egrep -hv "^[[:space:]]*(/|\*)" *.cpp *.h
or blank lines:
egrep -hv "^[[:space:]]*$" *.cpp *.h
or both, giving us a rough lines of code estimate:
egrep -hv "^[[:space:]]*(/|\*)" *.cpp *.h | egrep -v "^[[:space:]]*$" | wc -l
For that though, you should be using find and xargs again, since it would be faster, but this is easier to remember.
Happy CLIing.