How to find all file extensions recursively from a directory?

BashTerminalGrep

Bash Problem Overview


What command, or collection of commands, can I use to return all file extensions in a directory (including sub-directories)?

Right now, I'm using different combinations of ls and grep, but I can't find any scalable solution.

Bash Solutions


Solution 1 - Bash

How about this:

find . -type f -name '*.*' | sed 's|.*\.||' | sort -u

Solution 2 - Bash

list all extensions and their counts of current and all sub-directories

ls -1R | sed 's/[^\.]*//' | sed 's/.*\.//' | sort | uniq -c

Solution 3 - Bash

find . -type f | sed 's|.*\.||' | sort -u

Also works on mac.

Solution 4 - Bash

if you are using Bash 4+

shopt -s globstar
for file in **/*.*
do
  echo "${file##*.}
done

Ruby(1.9+)

ruby -e 'Dir["**/*.*"].each{|x|puts x.split(".")[-1]}' | sort -u

Solution 5 - Bash

Yet another solution using find (that should even sort file extensions with embedded newlines correctly):

# [^.]: exclude dotfiles
find . -type f -name "[^.]*.*" -exec bash -c '
  printf "%s\000" "${@##*.}"
' argv0 '{}' + |
sort -uz | 
tr '\0' '\n'

Solution 6 - Bash

Boooom another:

find * | awk -F . {'print $2'} | sort -u

Solution 7 - Bash

ls -1 | sed 's/.*\.//' | sort -u

Update: You are correct Matthew. Based on your comment, here is an updated version:

ls -R1 | egrep -C 0 "[^\.]+\.[^\./:]+$" | sed 's/.*\.//' | sort -u

Solution 8 - Bash

I was just quickly trying this as I was searching Google for a good answer. I am more Regex inclined than Bash, but this also works for subdirectories. I don't think includes files without extensions either:

ls -R | egrep '(\.\w+)$' -o | sort | uniq -c | sort -r

Solution 9 - Bash

Another one, similar to others but only uses two programs (find and awk)

find ./ -type f -name "*\.*" -printf "%f\n" | awk -F . '!seen[$NF]++ {print $NF}'

-type f restricts it to just files, not directories

-name "*\.*" ensures the filename has a . in it.

-printf "%f\n" prints just the filename, not the path to the filename.

-F . makes awk utilize a period as the field separator.

$NF is the last field, separated by periods.

!seen[$NF]++ evaluates to true the first time an extension is encountered, and false every other time it is encountered.

print $NF prints the extension.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMatthewView Question on Stackoverflow
Solution 1 - BashthkalaView Answer on Stackoverflow
Solution 2 - BashmindonView Answer on Stackoverflow
Solution 3 - BashmarcosdsanchezView Answer on Stackoverflow
Solution 4 - BashkurumiView Answer on Stackoverflow
Solution 5 - BashtoolyView Answer on Stackoverflow
Solution 6 - BashackuserView Answer on Stackoverflow
Solution 7 - BashTimeDeltaView Answer on Stackoverflow
Solution 8 - BashMehcs85View Answer on Stackoverflow
Solution 9 - BashRusty LemurView Answer on Stackoverflow