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.

Suppose that I have a folder containing .txt, .pdf, and other files. I would like to list the "other" files (i.e., files not having the extensions .txt or .pdf). Do you have any advice on how to do this?

I know how to list files not having a given extension. For example, if I want to list all files except the .txt files, then either

find -not -iname "*.txt"

or

ls | grep -v '\.txt$' | column

seem to work. But, how can I list everything except .txt files or .pdf files? It seems that I need to use some sort of logical "or" in find or grep.

share|improve this question
2  
Keep in mind that the behavior of ls vs find vs globbing may differ for hidden dotfiles. –  jw013 Sep 4 '12 at 19:32
1  
Another thing to keep in mind: find will traverse subdirectories, like a recursive ls. Use -maxdepth 1 with find to get it to behave more like ls. –  jw013 Sep 4 '12 at 20:20
    
So no recursive, just list the files in current dir ? –  warl0ck Sep 5 '12 at 7:20
add comment

6 Answers 6

up vote 7 down vote accepted

Assuming one has an appropriate version of ls, this is possibly the simplest way:

ls -I "*.txt" -I "*.pdf"

If you want to iterate across all the subdirectories:

ls -I "*.txt" -I "*.pdf" -R
share|improve this answer
4  
GNU ls options aren't portable. –  bahamat Sep 5 '12 at 20:45
3  
ls doesn't really belong in portable scripts anyways, so I've been assuming the OP is asking about interactive use only. –  jw013 Sep 5 '12 at 20:53
add comment

Find supports -o

find . ! '(' -name '*.txt' -o -name '*.pdf' ')'

You need the parenthesis to make the precedence right. Find does a lot of stuff; I suggest reading through its manpage.

You can also do an or in grep (but really, you should not parse the output of ls)

ls | grep -Ev '\.(txt|pdf)$' | column
share|improve this answer
    
Thanks! Why shouldn't I parse the output of ls? –  Andrew Sep 4 '12 at 19:36
4  
@Andrew first, because its fragile (consider a file name with a newline in it—yes, that's a valid file name—find's -print0/-exec/-delete/etc. avoid that problem); second, because there is usually an easier way. –  derobert Sep 4 '12 at 19:38
add comment

With bash extended globbing (turn on with shopt -s extglob), the glob !(*.txt|*.pdf) should work. You can pass this glob directly to any command that takes file arguments, including but not limited to ls.

share|improve this answer
add comment

In zsh with extendedglob:

print -rl -- *~*.(txt|pdf)

or

print -rl -- ^*.(txt|pdf)

Or with kshglob (yes, that is ksh globbing not "bash extended globbing"):

print -rl -- !(*.txt|*.pdf)

Remember though that those also exclude dot files.

ksh93 has the FIGNORE (mis)feature:

FIGNORE='@(.|..|*.txt|*.pdf)'
printf '%s\n' *
share|improve this answer
add comment
find /path/to/directory '!' -name '*.pdf' '!' -name '*.txt'

This is equivalent to the command with the OR operator, because of the De Morgan's laws.

share|improve this answer
add comment

As suggested by derobert, your best bet is to use find. However, you can in fact use the result in a pipeline with other commands.

GNU (and some BSD's) find support the -print0 predicate which tells it to print the filename terminated by a NUL character, which character isn't allowed within a file name and guarantees there won't be a collision. Other commands can be instructed to use the NUL as their input delimiter.

The most important of which is GNU xargs, which runs the command you specify and passes to it the list of files as command line arguments. You want to run xargs -r0 in conjunction with find's -print0 For example:

find . -type f ! \( -name \*.pdf -o -name \*.txt \) -print0 | xargs -r0 ls -ld

This safely prints a long directory listing of all the pdf and txt files, including those with spaces or unprintable characters in the name.

You can also use it with GNU tar as follows:

tar -zcf myarchive.tar.gz --null --files-from <(
  find . -type f ! -name \*.tar.gz -print0)

This builds a tar.gz file of all the files whose names don't end in .tar.gz

rsync also accepts null-delimited files with the -0 parameter, as do several others. But xargs is the glue you'll usually use for this type of purpose. Either that or find's -exec feature.

share|improve this answer
add comment

Your Answer

 
discard

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.