List files not matching a pattern?

BashWildcardGlobLs

Bash Problem Overview


Here's how one might list all files matching a pattern in bash:

ls *.jar

How to list the complement of a pattern? i.e. all files not matching *.jar?

Bash Solutions


Solution 1 - Bash

Use egrep-style extended pattern matching.

ls !(*.jar)

This is available starting with bash-2.02-alpha1. Must first be enabled with

shopt -s extglob

As of bash-4.1-alpha there is a config option to enable this by default.

Solution 2 - Bash

ls | grep -v '\.jar$'

for instance.

Solution 3 - Bash

Little known bash expansion rule:

ls !(*.jar)

Solution 4 - Bash

With an appropriate version of find, you could do something like this, but it's a little overkill:

find . -maxdepth 1 ! -name '*.jar'

find finds files. The . argument specifies you want to start searching from ., i.e. the current directory. -maxdepth 1 tells it you only want to search one level deep, i.e. the current directory. ! -name '*.jar' looks for all files that don't match the regex *.jar.

Like I said, it's a little overkill for this application, but if you remove the -maxdepth 1, you can then recursively search for all non-jar files or what have you easily.

Solution 5 - Bash

POSIX defines non-matching bracket expressions, so we can let the shell expand the file names for us.

ls *[!j][!a][!r]

This has some quirks though, but at least it is compatible with about any unix shell.

Solution 6 - Bash

If your ls supports it (man ls) use the --hide=<PATTERN> option. In your case:

$> ls --hide=*.jar

No need to parse the output of ls (because it's very bad) and it scales to not showing multiple types of files. At some point I needed to see what non-source, non-object, non-libtool generated files were in a (cluttered) directory:

$> ls src --hide=*.{lo,c,h,o}

Worked like a charm.

Solution 7 - Bash

And if you want to exclude more than one file extension, separate them with a pipe |, like ls test/!(*.jar|*.bar). Let's try it:

$ mkdir test
$ touch test/1.jar test/1.bar test/1.foo
$ ls test/!(*.jar|*.bar)
test/1.foo

Looking at the other answers you might need to shopt -s extglob first.

Solution 8 - Bash

Another approach can be using ls -I flag (Ignore-pattern).

ls -I '*.jar'

Solution 9 - Bash

One solution would be ls -1|grep -v '\.jar$'

Solution 10 - Bash

Some mentioned variants of this form:

ls -d *.[!j][!a][!r]

But this seems to be only working on bash, while this seems to work on both bash and zsh:

ls -d *.[^j][^a][^r]

Solution 11 - Bash

ls -I "*.jar"

> -I, --ignore=PATTERN
> do not list implied entries matching shell PATTERN

  • It works without having to execute anything before
  • It works also inside watch quotes: watch -d 'ls -I "*.gz"', unlike watch 'ls !(*.jar)' which produces: sh: 1: Syntax error: "(" unexpected

> Note: For some reason in Centos requires quoting the pattern after -I while Ubuntu does not

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
QuestioncalebdsView Question on Stackoverflow
Solution 1 - BashChristianView Answer on Stackoverflow
Solution 2 - BashMichael Krelin - hackerView Answer on Stackoverflow
Solution 3 - BashccartonView Answer on Stackoverflow
Solution 4 - BashDan FegoView Answer on Stackoverflow
Solution 5 - BashstefanctView Answer on Stackoverflow
Solution 6 - BashemveeView Answer on Stackoverflow
Solution 7 - BashJames BrownView Answer on Stackoverflow
Solution 8 - BashDivyanshu SrivastavaView Answer on Stackoverflow
Solution 9 - BashfgeView Answer on Stackoverflow
Solution 10 - BashThomas DebesseView Answer on Stackoverflow
Solution 11 - BashMarinos AnView Answer on Stackoverflow