1

Let' say I have the following directories tree:

.
├── 11
├── 111
├── 112
├── 1121
├── 113
├── 11a
├── 11a1
├── 1a1
├── 1a2
├── 1aa1.png
├── 2a1
├── a.
├── a1a
├── a1a.jpg
├── a2a
├── aa
├── -aa
├── aa.gif
├── aa.jpg
├── aa.png
├── aa.tiff
├── a.exe
├── a.gif
├── a.html
├── a.jpg
├── a.png
├── a.tiff
├── b2
├── b2a
├── ba.gif
├── ba.jpg
├── ba.png
├── ba.tiff
├── b.html
├── cb1.png
├── d.gif
└── sub1
    ├── d.gif
    └── sub2

So, if I want to match everything in . ($PWD) but files with ".jpg" extension, I do the following:

ls !(*.jpg)

But it outputs:

11   1121  11a1  1aa1.png  a1a  aa.gif   a.exe   a.png   b2a     ba.tiff  d.gif
111  113   1a1   2a1       a2a  aa.png   a.gif   a.tiff  ba.gif  b.html   .gif
112  11a   1a2   a.        aa   aa.tiff  a.html  b2      ba.png  cb1.png

sub1:
.  ..  d.gif  sub2

I'd like to exclude both sub1 and all of its contents (even directories), ie:

sub1:
.  ..  d.gif  sub2

I can exclude using:

GLOBIGNORE='sub1' before of ls, but what happens if I have more directories with differents names?

Is there a way to do that?

  • Sub1 didn't match jpg, so ls saw it as an argument. – Jeff Schaller Jun 26 '17 at 19:58
3

Take away solution:

ls -pd !(*.jpg) | grep -Ev /$

Details

Your glob pattern does not descend into sub-directories. ls does: if you pass a directory on its command line, it displays the content of that directory.

Replace ls by echo:

$ echo !(*.jpg)

Or, an (almost) equivalent solution: tell ls not to descend into directories.

$ ls -d !(*.jpg)

To answer the question on the comments: "how to use ls command to list only files and not folders?" there are a couple of options:

The short way to list only directories is:

echo */

So, maybe, the answer to list only files and not directories may use a grep -v

$ ls -p | grep -Ev /$

$ find . -maxdepth 1 ! -type d

Note that find will list dot-files (hidden files). And find . -type d will (also) not follow symlinks.

Or the pragmatic (posix) loop:

$ for f in * ; do if [ -f "$f" ] ; then echo "$f" ; fi ; done

Of course, the specific solution for a negated expansion should be like this:

$ ls -pd !(*.jpg) | grep -Ev /$
11
111
112
1121
113
11a
11a1
1a1
1a2
1aa1.png
2a1
a.
a1a
a2a
aa
aa.gif
aa.png
aa.tiff
a.exe
a.gif
a.html
a.png
a.tiff
b2
b2a
ba.gif
ba.png
ba.tiff
b.html
cb1.png
d.gif

Note:

Option -p along with grep does the trick to identify directories, take a look at ls man page:

-p, --file-type, --indicator-style=file-type

|improve this answer|||||
  • You're right, echo is better than ls, however does not exclude directories, I can use find ./ -maxdepth 1 -type f ! -name "*.jpg" . But I keep on wondering if there is a "bash way" to do it... – sebelk Jun 27 '17 at 10:27
  • 1
    That is not waht you ask in the title of the question. Globbing (not using globstar's **) does not go inside directories. What you are asking now is actually this title: "how to use ls command to list only files and not folders?". – Isaac Jun 27 '17 at 16:51
  • I've edited the title. – sebelk Jun 28 '17 at 16:18
3

Must the shell be bash? With ZSH and EXTENDED_GLOB set one can use (.) to qualify () a glob expression to just plain files .:

$ PS1='%% ' zsh -f
% setopt EXTENDED_GLOB
% mkdir foo && cd foo
% touch nope.jpg
% mkdir sub1
% touch asdf
% print ^*.jpg
asdf sub1
% print ^*.jpg(.)
asdf
% 
|improve this answer|||||
  • Nice answer, I've modified subject, because I am looking for how to do it using bash, perhaps that is not possible... – sebelk Jun 27 '17 at 10:29
-1

You can pipe the output of ls into a grep filter such as:

ls * | grep -v "jpg"

To return all matched that do not contain the string "jpg". (The grep -v flag inverts the match, returning every line that does not contain the specified string.)

|improve this answer|||||
-1

I can exclude using: GLOBIGNORE='sub1' before of ls, but what happens if I have more directories with differents names?

From the Bash reference manual, GLOBIGNORE is:

A colon-separated list of patterns defining the set of filenames to be ignored by filename expansion. If a filename matched by a filename expansion pattern also matches one of the patterns in GLOBIGNORE, it is removed from the list of matches. The pattern matching honors the setting of the extglob shell option.

|improve this answer|||||

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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