glasses
glasses

Reputation: 763

issue with piping find into sed (find and replace)

Here is my current code, my goal is to find every file in a given directory (recursively) and replace "FIND" with "REPLACEWITH" and overwrite the files.

FIND='ALEX'
REPLACEWITH='<strong>ALEX</strong>'

DIRECTORY='/some/directory/'

find $DIRECTORY -type f -name "*.html" -print0  |
 LANG=C xargs -0  sed  -i "s|$FIND|$REPLACEWITH|g" 

The error I am getting is:

sed: 1: "/some/directory ...": command a expects \ followed by text

Upvotes: 3

Views: 2098

Answers (2)

Charles Duffy
Charles Duffy

Reputation: 295954

As given in BashFAQ #21, you can use perl to perform search-and-replace operations with no potential for data being treated as code:

in="$FIND" out="$REPLACEWITH" find "$DIRECTORY" -type f -name '*.html' \
  -exec perl -pi -e 's/\Q$ENV{"in"}/$ENV{"out"}/g' '{}' +

If you want to include only files matching the FIND string, find can be told to only pass files which grep flags on to perl:

in="$FIND" out="$REPLACEWITH" find "$DIRECTORY" -type f -name '*.html' \
  -exec grep -F -q -e "$FIND" '{}' ';' \
  -exec perl -pi -e 's/\Q$ENV{"in"}/$ENV{"out"}/g' '{}' +

Because grep is being used to evaluate individual files, it's necessary to use one grep call per file so its exit status can be evaluated on a per-file basis; thus, the use of the less efficient -exec ... {} ';' action. For perl, it's possible to put multiple files to process on one command, hence the use of -exec ... {} +.

Note that fgrep is line-oriented; if your FIND string contains multiple lines, then files with any one of those lines will be passed to perl for replacements.

Upvotes: 2

Luis
Luis

Reputation: 1235

You can have find invoke sed directly although I think all the modification times on your files will be affected (which might matter or not):

find $DIRECTORY -type f -name "*.html" -exec sed -i "s|$FIND|$REPLACEWITH|g" '{}' ';'

Upvotes: 1

Related Questions