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 have a directory which contains all the C programs. I have also compiled them at the creation time so as to check my code. There are a lot of programs nearly 100 of them. So I want a BASH script that would check whether the file ends with ".c". If it does then it does not do anything, which if it doesn't then it would remove them. Suppose one of my file name is "Hello.c" then I have named its binary during compile time as "Hello". So can anyone make this script for me?

share|improve this question

4 Answers 4

up vote -5 down vote accepted

There's probably a neater way, but you can do

ls | grep -v '\.c$' | xargs rm
share|improve this answer
This won't work with filenames containing spaces! –  Bernhard Apr 4 '13 at 13:01
I don't know how this answer got two upvotes. Bernhards solution is much better. It's shorter and doesn't fail on the very common case “file names with spaces”. We're dealing with rm here, and this answer may easily delete a .c file which it is not supposed to. I consider it dangerous. –  Marco Apr 4 '13 at 16:50
@h3rrmiller: ls automatically acts as if -1 were specified, if the output is a pipe. Another (mutant) alternative is find . -not -name '*.c' -print0 | xargs -0 rm which avoids the spaces problem, but also avoids having to remember myriad find options (and so is more general). –  Norman Gray Apr 4 '13 at 18:34
@PranitBauva The reason it got downvotes, is that is not completely safe. You may end up deleting the wrong files! –  Bernhard Apr 13 '13 at 7:45

You can use the -delete flag of find (first test with -ls)

find -not -name "*.c" -delete

If you do not want to use find, but only files in the current directory, you could do

rm !(*.c)

Make sure in bash that with shopt -s extglob the correct globbing is set. Additionally, if you have globstar set (shopt -s globstar), you can act recursively (for bash versions above 4):

rm **/!(*.c)
share|improve this answer
Can also add -maxdepth 1 to the find to stop recursion. –  Flup Apr 4 '13 at 12:56
even this works. clean and simple!! –  Pranit Bauva Apr 4 '13 at 13:05
-delete is not portable if that matters, you can use -exec rm instead. –  Chris Down Apr 8 '13 at 2:12
If rm !(*.c) generates event not found in bash, it needs to be proceeded with shopt -s extglob. –  kenorb Mar 14 at 18:48

I always prefere find in such cases. You have not worry about special filenames and you may handle different filetypes. If you like to delete files only, you should add -type f to your options. So my favorite solution is:

find -not -name "*.c" -type f -delete

But be careful. Testing such commands is always a good idea.

share|improve this answer

Activate ksh extended wildcard patterns with the command shopt -s extglob (you can put this in your ~/.bashrc). Then the pattern !(*.c) matches all files in the current directory except the ones that match *.c:

rm !(*.c)

This is pretty dangerous as it deletes .txt files, .h files, etc. A less drastic command would be

rm !(*.*) *.o

This still deletes README, Makefile, etc. You should maybe only delete executable files:

find . -type d -prune -o -type f -perm -100 -exec rm {} +

Shorter, if your find supports it:

find . -maxdepth 1 -type f -perm -100 -delete
share|improve this answer

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.