Xargs Command – Fixing Failing String Replacement

findlinuxscriptingsedxargs

I am trying to automatically create symlinks to directories and to replace a certain text string (foo) of the original name in the link name (bar). The name of the target directories may contain spaces (e.g. "foo with whitespace").

This is a minimal example:

> ls -Q

"foo"
"foo with whitespace"

I am trying the following command (using xargs' -p option to debug/check the result before its execution):

find . -iname "*foo*" -print0 | xargs -0 -I '{}' -p ln -s {} `echo {} | sed -e s#foo#bar#g

My desired outcome is this:

ln -s 'foo with whitespace' 'bar with whitespace'
ln -s 'foo' 'bar'

However the string replacement fails. The actual output looks like this:

ln -s './foo with whitespace' './foo with whitespace'?...
ln -s ./foo ./foo?...

If I use constant strings instead of {}, the string replacement works as intended:

find . -iname "*foo*" -print0 | xargs -0 -I '{}' -p ln -s {} `echo "foo" | sed -e s#foo#bar#g`

ln -s './foo with whitespace' bar?...
ln -s ./foo bar?...

What's my mistake and how do I fix the command invocation?

Edit: Tilman Schmidt's solution works like a charm.

Best Answer

The command substitution

`echo {} | sed -e s#foo#bar#g`

is performed only once, before the pipeline composed of the find and xargs commands is run. So sed gets the string {} as input and, not finding foo, outputs it unchanged. Then the shell runs the resulting command line

find . -iname "*foo*" -print0 | xargs -0 -I '{}' -p ln -s {} {}

So command substitution is the wrong tool here. Try something like

find . -iname "*foo*" -print | while read f; do ln -s "$f" "${f//foo/bar}"; done

(untested) instead. Note that this won't work if you have filenames containing newlines.