I am playing around with -exec flag of find command. I am trying to use the flag to print the extension name of files, using a fairly new Linux distribution release.

Starting simple, this works:

find . -type f -exec echo {} \;

An attempt to use convenient Bash string feature fails:

find . -type f -exec echo "${{}##*.}" \; (bad substitution)

So what should be the correct way to do it?

  • Although it won't help in this case, read about the -printf option in the find man page -- it can tell you a lot of information about the files found. Jan 13, 2015 at 15:03

2 Answers 2


If you want to use shell parameter expansion then run some shell with exec:

find . -type f -exec sh -c 'echo "${0##*.}"' {} \;
  • 1
    I get bad substitution with this still, but it works if you use bash instead of sh Jan 13, 2015 at 15:05
  • It is possible if your sh is a link to the shell which doesn't support parameter expansion. bash is good choice.
    – jimmij
    Jan 13, 2015 at 15:07
  • Correct jimmij, just pointing out that not all sh are equal. Does exec create a new shell child process each time? Jan 13, 2015 at 15:10
  • @CaptainLepton Yes, you can find out with find . -type f -exec sh -c 'echo "$$"' {} \;
    – jimmij
    Jan 13, 2015 at 15:13
 find . -type f | while read file; do echo "${file##*.}"; done

Has the advantage of bypassing all that tricky exec syntax that no one can ever remember.

Also has the advantage of only creating one child process, instead of creating and destroying one for every found file. This is considerably faster than jimmij's version using exec

# Preparation - 1000 files
for i in {1..1000}; do touch $i.$i; done

>time find . -type f -exec sh -c 'echo "${0##*.}"' {} \;
real    0m21.906s
user    0m6.013s
sys     0m12.304s

>time find . -type f | while read file; do echo "${file##*.}"; done
real    0m0.219s
user    0m0.125s
sys     0m0.087s

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .