It looks like 'find', 'bash' and 'sed' in some cases does not work as one expects.

The following example should first create file 'sample.txt', then find the file and finally process it by '-exec' command. The executed command prints found filename, test specimens, and modified filename. The 'sed' command itself is used to replace 'txt' to 'TXT'.

touch sample.txt
find ./ -maxdepth 1 -name "*.txt" -exec echo {} $(echo Specimen_before.txt {} Specimen_after.txt |sed -e "s/txt/TXT/g") \;

The expected output is:

./sample.txt Specimen_before.TXT ./sample.TXT Specimen_after.TXT

Instead it produces:

./sample.txt Specimen_before.TXT ./sample.txt Specimen_after.TXT

(the example has been tested also with old-school command substitution through backquotes '`' with the same result)

What am I doing wrong?

  • Why do you expect sed -e "s/txt/TXT/g" to leave alone the second txt? {} is expanded before sed gets to see it. Oct 30 '17 at 10:58
  • Not sure I understand your question. I expect the "echo Specimen_before.txt {} Specimen_after.txt" gets printed by the echo, then it gets processed by the sed "|sed -e "s/txt/TXT/g"". I assume the {} expansion is done before the whole '-exec' command is invoked. Oct 30 '17 at 11:06
  • Ok, not I got it. Both Satō Katsura and Kusalananda answers lead me to understand that the "command substitution is executed before find even starts". Oct 30 '17 at 11:14
  • I actually think @SatōKatsura's comment is slightly wrong. {} is expanded by find, after sed has seen it ("it" being the string {}).
    – Kusalananda
    Oct 30 '17 at 11:39
  • You are right, the sed is invoked before find due to the shell command substitution, which is done before the find is executed by the shell. Oct 30 '17 at 12:12

The command substitution is executed before find even starts. The actual command executed (after substitutions, expansions and quote removals etc.) is

find ./ -maxdepth 1 -name *.txt -exec echo {} Specimen_before.TXT {} Specimen_after.TXT ;

If you need to run anything fancy (pipes or multiple commands) with -exec, then start a separate shell to do it:

find . -maxdepth 1 -type f -name '*.txt' \
    -exec sh -c 'printf "%s " "$1"; printf "%s %s %s\n" "before.txt" "$1" "after.txt" | sed "s/txt/TXT/g"' sh {} ';'
  • I got it. Your "The command substitution is executed before find even starts." is the answer. Oct 30 '17 at 11:12

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