2

rm -rf .* will only not end horribly because rm refuses to delete . and ...

How do I exclude these special directories from a glob pattern?

This is not solved by dotglob since I want to match only files beginning with a dot not all files.

marked as duplicate by Jeff Schaller, jimmij, don_crissti, αғsнιη, user88036 Oct 18 '18 at 10:15

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

2

With bash, setting the GLOBIGNORE special variable is some non-empty value is enough to make it ignore . and .. when expanding globs. From the Bash docs:

The GLOBIGNORE shell variable may be used to restrict the set of filenames matching a pattern. If GLOBIGNORE is set, each matching filename that also matches one of the patterns in GLOBIGNORE is removed from the list of matches. If the nocaseglob option is set, the matching against the patterns in GLOBIGNORE is performed without regard to case. The filenames . and .. are always ignored when GLOBIGNORE is set and not null. However, setting GLOBIGNORE to a non-null value has the effect of enabling the dotglob shell option, so all other filenames beginning with a ‘.’ will match.

If we set it to .:.., both . and .. will be ignored. Since setting it to anything non-null will also get this behaviour, we might as well set it to just .

So:

GLOBIGNORE=.
rm -rf .*

(From my earlier answer on Ask Ubuntu.)

  • I think this is the best way to solve the problem I was having since it specifically excludes the special cases. – Franklin Oct 18 '18 at 10:54
4

This should do it:

rm -rf .[^.] .??*  

This command can catch all cases.

.[^.] will catch any two character entries. .??* will only match 3+ character filenames.

1

You need to exclude single and double dots:

rm -rf {..?,.[!.]}*
  • ..?* matches everything with two dots followed by anything
  • .[!.]* matches everything with single dot not followed by another dot
1

With extglob:

$ shopt -s extglob
$ touch ... .a .bbb ..c foo
$ echo .!(.|)
... .a .bbb ..c

!(.|) matches anything but a dot or empty, so .!(.|) matches anything starting with a dot, except . and ...

1

@Goro has, I think, the simplest totally correct answer. However, I find it's a pain to type. I would suggest instead

ls .??*

It's absolutely true that this will miss files like .a, but those are so extraordinarily rare that in practice I don't think it matters, especially for interactive usage.

1

With find, it's a bit more to type but you can be more explicit, which makes it easier to understand:

find . -maxdepth 1 -name '.*' ! -name . ! -name .. -exec rm -rf {} +

This calls rm -rf with all names in the current directory (only) that starts with a dot and which is not . nor ... I'm not using -delete here as that predicate may not delete non-empty directories.

If you want to match only regular files in the current directory, it becomes even easier:

find . -maxdepth 1 -type f -name '.*' -delete

Here, we don't need to exclude . or .. as they are automatically excluded by -type f.

Change -type f to ! -type d to make it delete any non-directory (i.e. symbolic links etc.)

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