Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I would like to recursively list all files in a directory which match a given input pattern using ls. (Oh, and this is in Bash).

The only way I can think of to do this using ls is to shopt -s extglob and then use the ls --ignore=pattern option with the extended glob negate pattern !( pattern-list ). Seems kind of roundabout to negate in an ignore but otherwise wildcards don't recurse.

Assuming I already have the directory to look in as dir and the pattern to match against as file, this is my current attempt:

ls -AR --ignore='!('"${file}"')' ${dir}

However, this does not seem to have the desired effect, for example in a test case where the directory structure gives this output for ls -AR:

    a  a.c  b  b.c  c  c.h  s

    a  a.c  b  b.c  c  c.h

My command with file = *.c and dir = s (full command: ls -AR --ignore=!(*.c) s) gives this output:

    a  a.c  b  b.c  c  c.h

I know I could pipe it into grep quite easily, and do something like ls -AR s | grep -v '.*.c' and get output like:


but I am looking for a way to do this just through ls.

Sorry if I was unclear at all, this is my first time on a StackExchange site.

Thanks in advance!

share|improve this question

2 Answers 2

up vote 2 down vote accepted

What about adding the globstar shell option and the -d flag to ls to not automatically list directory contents?

shopt -s extglob
shopt -s globstar
ls -d  **/!(*.c)
share|improve this answer
Great suggestion, I didn't know they added globstar. However, since I am looking to match the input pattern (which wasn't clear, fixed that), not omit it I think I need to use ls -dAR **/*.c. –  suess2.0 Feb 13 '13 at 22:01
Note that contrary to zsh (where that feature originated) and ksh93 (where bash copied it from), bash's ** follows symbolic links when descending the directory tree, which can have serious unwanted effects. So use with care or use find or other shells if there's a risk that there be symlinks in the directory. –  Stéphane Chazelas Feb 13 '13 at 22:10
@choroba, the -A and -R options don't make sense in combination with -d and can be removed (the shell makes up the list of files here, not ls, ls in that case is just outputting the list so could be replaced with printf '%s\n'). Also, you need -- to mark the end of options or write it ls -dAR ./**/!(*.c) to avoid problems with filenames starting with -. Also, if there's no c file and if there's a directory called ** and a file called !(*.c) inside it, ls will report it even though it's not a c file. You may want to add the failglob option. –  Stéphane Chazelas Feb 13 '13 at 22:34
The symlink issue has now been fixed in bash-4.3 –  Stéphane Chazelas Mar 18 at 13:45

GNU ls --ignore option takes standard file name patterns, not a ksh extended one (what bash (and bash only, not ls) recognises with extglob)

So you can do:

ls -AR --ignore='*.[ch]' --ignore='*.cpp'

But you can't do:

ls -AR --ignore='!(*.[ch]|*.cpp)'

Which would just ignore files named like: !(foo.c|bar.cpp).

In ls -AR --ignore=!(*.c), since the pattern is not quoted, bash would try to expand it before passing it to ls. So it would look in the current directory for files whose name starts with "--ignore=" and doesn't end in ".c". For instance, if there were two files, one called --ignore=foo and one called --ignore=bar, it would run ls with those four arguments: "ls", "-AR", "--ignore=bar", "--ignore=foo".

You can do (with GNU find).

find . -regextype posix-extended -regex '.*\.([ch]|cpp)' -ls

Or with any POSIX find:

find . \( -name '*.[ch]' -o -name '*.cpp' \) -exec ls -ld {} +

Or assuming there's no symlinks to directories:

shopt -s extglob globstar dotglob failglob
ls -ld -- **/*.@([ch]|cpp)

This time, bash does expand the globbing pattern and passes the list of files to ls.

share|improve this answer
Thanks for the extra information, that explains extglob nicely. –  suess2.0 Feb 13 '13 at 22:07

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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