1

I have a script that searches for a string and replaces it via the sed command. If that string contains special characters, the script will escape them (except for the slash because it's my current delimiter for sed and the column because it marks the string on the bash line).

Here it goes:

raw_searchstring='SearchForThis';
raw_replacementstring='ReplaceWithThis';

#Escape special characters:
quoted_searchstring=$(printf %s "$raw_searchstring" | sed 's/[][()\.^$?*+]/\\&/g');
quoted_replacementstring=$(printf %s "$raw_replacementstring" | sed 's/[][()\.^$?*+]/\\&/g');

find ./ -type f -exec sed -i -r "s/$quoted_searchstring/$quoted_replacementstring/" {} \;

I tested this on Ubuntu and it worked just fine.

However, I need to run the script on a AIX system. Since it does not support in-line editing with sed -i I tried the following, as suggested on a similar question here (AIX's sed - in place editing):

find ./ -type f -exec sed -r 's/$quoted_searchstring/$quoted_replacementstring/' infile > tmp.$$ && mv tmp.$$ infile {} \;

This is where I get the error

find: missing argument to `-exec'

so I tried to pass more than one -exec statement to find with this line:

find /home/tobias/Desktop -type f -exec sed -r 's/$quoted_searchstring/$quoted_replacementstring/' infile > tmp.$$ {} \; -exec mv tmp.$$ infile {} \;

that doesn't work either:

sed: can't read infile: No such file or directory

I am not sure what I did wrong. Can you help me fix this line of code or point me in the right direction?

  • Try replacing "infile" with "{} \;" and removing trailing "{}" in your example – fpmurphy Apr 20 '16 at 15:00
  • 2
    since you have multiple commands that you want to exec, but exec just wants one, tell exec to use 'sh' and tell 'sh' to do those commands. – Jeff Schaller Apr 20 '16 at 15:01
  • Useful reading re: temp files and redirection: unix.stackexchange.com/a/186126/117549 – Jeff Schaller Apr 20 '16 at 15:07
2

Your attempts aren't working because you're trying to use shell operators such as && and > in the command executed by find, but you're typing those operators directly in the command, so they're executed by the shell that's calling find. Your commands are parsed as

find … > tmp.$$ && mv …

e.g. the first find invocation is

find ./ -type f -exec sed 's/$quoted_searchstring/$quoted_replacementstring/' infile

with output redirected to tmp.$$. There are other problems with the command: infile should be {} (it's the file that find found), and the single quotes around the sed expression should be double quotes since you're using shell variables in there.

Since you need to use shell constructs in the command that's executed by find, tell find to execute a shell.

find … -exec sh -c '…' {} \;

To avoid quoting difficulties, pass things that need quoting, such as the sed expression, as arguments to sh.

find ./ -type f -exec sh -c '
    sed "$0" "$1" >"$1.new" && mv "$1.new" "$1"
  ' "s/$quoted_searchstring/$quoted_replacementstring/" {} \;

For a slight performance gain at a slight legibility loss, you can use the -exec … {} + form and a shell loop.

find ./ -type f -exec sh -c '
    for x; do
      sed "$0" "$x" >"$x.new" && mv "$x.new" "$x";
    done
  ' "s/$quoted_searchstring/$quoted_replacementstring/" {} +

Alternatively, if the version of ksh93 on your AIX isn't too ancient, you can use its recursive globbing feature (introduced in ksh93p).

set -G
for x in **; do
  [[ -f $x ]] || continue
  sed "s/$quoted_searchstring/$quoted_replacementstring/" "$x" >"$x.new" && mv "$x.new" "$x";
done

In any case, note that you'll also need to tweak your string-to-regexp conversion to produce a basic regular expression since the -r flag to use ERE is a GNU extension. Also your existing code has a few bugs: you forgot to quote slashes, and you aren't quoting the right characters in the replacement text.

quoted_searchstring=$(printf %s "$raw_searchstring" | sed 's![[\/.^$*]!\\&!g');
quoted_replacementstring=$(printf %s "$raw_replacementstring" | sed -e 's![][\/&]!\\&!g' -e '!$ s!$!\\!');
  • Thanks a lot for the detailled answer, it is much appreciated. This worked perfectly for me. – Rich B. Goodman Apr 21 '16 at 12:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.