KRB
KRB

Reputation: 5175

Replace a string with another string in all files below my current dir

How do I replace every occurrence of a string with another string below my current directory?

Example: I want to replace every occurrence of www.fubar.com with www.fubar.ftw.com in every file under my current directory.

From research so far I have come up with

sed -i 's/www.fubar.com/www.fubar.ftw.com/g' *.php

Upvotes: 63

Views: 57955

Answers (6)

rubo77
rubo77

Reputation: 20835

If there are no subfolders, a simpler to remember way is

replace "www.fubar.com" "www.fubar.ftw.com" -- *

where * can also be a list of files.

You need this alias to work as easy as this:

 alias replace='function _replace() { from="$1"; to="$2"; shift 2; find . -type f \( -name "$@" \) -exec sed -i "s/${from}/${to}/g" {} +; }; _replace'

(this replace is working now like the SQL command in mySQL)

If you have hidden files with a dot you can add those to * with

shopt -s dotglob

If you only have one depth of subfolders you can use */* instead of *

replace "www.fubar.com" "www.fubar.ftw.com" -- */*

Upvotes: 0

Jimbobur
Jimbobur

Reputation: 101

A more efficient * alternative to the currently accepted solution:

grep "www.fubar.com" . -lr | xargs sed -i 's/www.fubar.com/www.fubar.ftw.com/g'

This avoids the inefficiency of the find . -exec method, which needlessly runs a sed in-place replacement over all files below your current directory regardless of if they contain the string you're looking for or not, by instead using grep -lr. This gets just the files containing the string you want to replace which you can then pipe to xargs sed -i to perform the in-place replacement on just those files.


* : I used time to make a cursory comparison of my method with the accepted solution (adapted for my own use case); The find . -exec-style method took 3.624s to run on my machine and my above proposed solution took 0.156s, so roughly 23x faster for my use case.

Upvotes: 8

Seweryn Niemiec
Seweryn Niemiec

Reputation: 1315

When using ZSH as your shell you can do:

sed -i 's/www.fubar.com/www.fubar.ftw.com/g' **/*.php

Upvotes: 0

martin clayton
martin clayton

Reputation: 78105

You're on the right track, use find to locate the files, then sed to edit them, for example:

find . -name '*.php' -exec sed -i -e 's/www.fubar.com/www.fubar.ftw.com/g' {} \;

Notes

  • The . means current directory - i.e. in this case, search in and below the current directory.
  • For some versions of sed you need to specify an extension for the -i option, which is used for backup files.
  • The -exec option is followed by the command to be applied to the files found, and is terminated by a semicolon, which must be escaped, otherwise the shell consumes it before it is passed to find.

Upvotes: 110

johnny
johnny

Reputation: 657

Solution using find, args and sed:

find . -name '*.php' -print0 | xargs -0 sed -i 's/www.fubar.com/www.fubar.ftw.com/g'

Upvotes: 3

bash-o-logist
bash-o-logist

Reputation: 6911

A pure bash solution

#!/bin/bash
shopt -s nullglob
for file in *.php
do
    while read -r line
    do
       echo "${line/www.fubar.com/www.fubar.ftw.com}"
    done < "$file" > tempo && mv tempo "$file"

done

Upvotes: 2

Related Questions