4

If I pipe ls to grep, how would I be able to return a list of files with the condition that they only have an x amount of letters, extension included?

So for example, if ls gives me:

abcde.jav a156e.exc test.c prog1.c qwert r.c 

and I'm looking for all files that contain strictly 5 letters, extensions included:

a156e.exc test.c prog1.c qwert

I've tried:

ls | grep '^[a-z]${5}'
ls | grep "^[a-z]|[a-z]$"

and other things like that, but I can't seem to get it. It seems like the solution should be really simple but I can't figure it out. Any help would be appreciated.

nsaftarli
  • 149
  • 1
  • 1
  • 10

1 Answers1

3

You could use the following expression:

Live Example

^([^a-z]*[a-z][^a-z]*){5}$

Explanation:

  • ^ - Anchor denoting the start of the string
  • ([^a-z]*[a-z][^a-z]*) - Group that matches a single letter between zero or more non-letter characters
  • {5} - Match the previous group 5 times
  • $ - Anchor denoting the end of the string.

Usage:

ls | grep -E '^([^a-z]*[a-z][^a-z]*){5}$'

Or without ls:

for f in *; do
  if [[ $f =~ ^([^a-z]*[a-z][^a-z]*){5}$ ]]
    then
      echo $f
  fi
done
Josh Crozier
  • 233,099
  • 56
  • 391
  • 304
  • 1
    Exactly what I was looking for. Thank you! – nsaftarli Feb 14 '16 at 06:50
  • 1
    Great regex. You can avoid `ls` without resorting to a (slow) loop: `printf '%s\n' * | grep -E '^([^a-z]*[a-z][^a-z]*){5}$'`. Or, using a process substitution: `grep -E '^([^a-z]*[a-z][^a-z]*){5}$' <(printf '%s\n' *)`. (This would still break with filenames with embedded newlines; with _GNU_ `grep`, this could be remedied as follows: `grep -zoE '^([^a-z]*[a-z][^a-z]*){5}$' <(printf '%s\0' *)`). – mklement0 Feb 16 '16 at 00:11