From time to time I need to find a culprit in an unknown dotfile and instead of trying to figure out which package is to be blamed (e.g. xfce4 or thunar?) and what is their naming convention (.app vs .application vs .some_old_name vs .config/app...), I just want to go for it the quick & dirty way:

me@here:~$ grep -IR .* -e culprit

But this quick & dirty way is also the silly way. After few minutes I figure out that .* means .. as well, and there we are. Sometimes I resort to probably even less quick & more dirty variant:

me@here:~$ grep -IR /home/me -e culprit

which turns out to be of a superior silliness, especially if I have some huge or distant mountains at my $HOME. Too bad that I can't figure out The Quick And Clean And Right Way. (And my disk heads are slowly wearing out.)

Is it possible to achieve this within wildcard expansion at all? I.e. a variant of .* that does not match .. (and ../.....)?


If you're on Linux, you can try

grep -IR --exclude-dir=".." culprit .*

Since you asked about shell wildcards, my first thought is .[!.]* ..[!.]*, as in

grep -IR culprit .[!.]* ..[!.]*

Which has the problem that grep will exit with an error if there are no files starting with ... To get around that, you can either add -s to grep to tell it to ignore missing files:

grep -IRs culprit .[!.]* ..[!.]*

Or use the bash and zsh nullglob option to make ..[!.]* expand to nothing if there are no files beginning with ..

shopt -s nullglob   # for bash
setopt nullglob     # for zsh

grep -IR culprit .[!.]* ..[!.]*

Failing all of the above, there's always find

find . -path "./.*" -exec grep culprit {} +
  • Including ..[!.]* seems a bit hysterical; do you actually have files whose names start with too dots? I would nominate grep -IR culprit .[!.]* as a reasonably correct, compact, portable, canonical answer. – tripleee Feb 9 '13 at 11:26
  • Absolutely. Just .[!.]* would deal with 99.9% of cases, and jordanm's suggested pattern is both correct and doesn't have the nullglob issue. – Mikel Feb 10 '13 at 1:39

If you have bash available, you can use extglob to get only dotfiles.

shopt -s extglob
grep -IR 'foo' .!(|.)

The ! operator in an extended glob is "not". This pattern matches anything starting with a dot, excluding '.' and '..'.


Thanks to this wiki, I found there is this GLOBIGNORE variable:

The Bash variable (not shopt) GLOBIGNORE allows you to specify patterns a glob should not match. This lets you work around the infamous "I want to match all of my dot files, but not . or .." problem:

$ echo .*
. .. .bash_history .bash_logout .bashrc .inputrc .vimrc
$ echo .*
.bash_history .bash_logout .bashrc .inputrc .vimrc

Nice thing is that this has almost no side effects (I mean, how often you really want to match .. and .?), so it would be even acceptable to export GLOBIGNORE=.:.. from .bashrc, and for manual tasks just use the old .* glob, as in the first example in the Q.

me@here:~$ set | grep GLOBIGNORE
me@here:~$ grep -IR .* -e culprit
.some-app/config: set culprit=1

I use this script to quickly edit dotfiles:

dirs=($HOME/.$1* $HOME/.$1/ $XDG_CONFIG_HOME/$1/)

read -r -d '' -a files /dev/null)
(( ${#files[*]} )) && "$EDITOR" "${files}"

A more POSIX version:

find "${dirs[@]}" -type f \( -name "*.conf" -o -name "conf" -o -name "config" \
-o -name "*rc" -o -name "*.
grep -IR culprit .[!.]*

.[!.]* matches all dot files except the ones that begin with ... Since dot files don't begin with .., that's ok.

Or you could use zsh, which never includes . or .. in a glob:

grep -IR culprit .*

More esoterically, in bash, if you have the extglob option turned on (shopt -s extglob), this works, and includes ..?* (it also works in ksh, and in zsh after setopt ksh_glob):

grep -IR culprit !(|.)

To solve your problem, try this instead (in bash), that matches everything but ..:

shopt -s dotglob; grep -e culprit -IR *

But actually you wanted this,

grep -e culprit -IR .

.* is unnecessary since you can start grep from current directory.

  • grep -e culprit -IR . is just another way of writing my second example. Which is what I want except that I want to exclude (from the expansion, not from the recursion), everything that does not start with dot and ... The point is to search in various package config files and avoid any real user data. – Alois Mahdal Feb 9 '13 at 3:03
  • BTW I don't understand the last sentence: as opposed to *, which does not match hidden files, .* does match them. Only there's a small "catch": it matches .. and . as well, which is very bad in my case. But what does .* have to do with "current directory"? – Alois Mahdal Feb 9 '13 at 3:05

Just found this one:

me@here:~$ grep -IR .[a-zA-Z0-9]* -e culprit

ugly as hell and not trivial to type, but seems to do the job. Most of the time you could omit the caps and digits:

me@here:~$ grep -IR .[a-z]* -e culprit

There must be a nicer way, though...

  • There is a better way. This will work in most cases, but won't match valid dot files such as '.,foo', if such a file existed. Your method is POSIX and will work in all shells, unlike mine. – jordanm Feb 9 '13 at 3:26
  • 2
    When you say [a-zA-Z0-9] you really mean "not a dot", which is written [!.]. – Mikel Feb 9 '13 at 5:13


grep -R -e culprit \.*

The backslash before the dot I think will make it a literal dot instead of treating dot as the meta character meaning match any atom.

  • 1
    Did you test your solution before posting it? – Mikel Feb 12 '13 at 5:01
  • Yes did but only very quickly – Jason Tan Feb 13 '13 at 9:33

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.