109

I want to glob every hidden file and directory, but not the current (.) and parent directory (..).

I am using bash.

Observe current behaviour:

$ ls -a
.  ..  ...a  ...aa  ..a  ..aa  .a  .aa  .aaa  a
$ echo *
a
$ echo .*
. .. ...a ...aa ..a ..aa .a .aa .aaa

I would like .* to behave like this

$ echo .*
...a ...aa ..a ..aa .a .aa .aaa

There is the shell option dotglob

$ shopt -s dotglob

that works in a way; now I can use * to glob everything (hidden or not) but not . and ..

$ echo *
...a ...aa ..a ..aa .a .aa .aaa a

but now I can't differentiate between hidden or not. Also, .* still globs . and ..

$ echo .*
. .. ...a ...aa ..a ..aa .a .aa .aaa

Is there a way to make .* not expand to . and ..?

Lesmana
  • 27,439

7 Answers7

93

You can use the GLOBIGNORE variable to hide the . and .. directories. This does automatically also set the dotglob option, so * now matches both hidden and non-hidden files. You can again manually unset dotglob, though, this then gives the behavior you want.

See this example:

$ ls -a
.  ..  a  .a  ..a
$ GLOBIGNORE=".:.."
$ shopt -u dotglob
$ echo * # all (only non-hidden)
a
$ echo .* # all (only hidden)
.a ..a

Note the following corner cases shown by numerous users in the comments:

  • . and .. are always ignored as long as GLOBIGNORE has a non-null value. GLOBIGNORE=":" ; echo .*, for instance, would work for the example above, even though it is less readable.
  • If you want to match hidden directories only (and not all hidden files) and therefore glob with a trailing slash, echo .*/, you need to set GLOBIGNORE='.:..:./:../', because the glob will match ./ and ../, which must be excluded explicitly to prevent them from showing up in the result.
akraf
  • 867
  • 1
    This only works if you include the glob in the command; env GLOBIGNORE=". .." ls -a returns . and .. but ... ls -ad .* doesn't. – gvkv Aug 24 '10 at 13:04
  • 3
    I don't get this comment... The question was not about the ls command, but about a glob -- "including the glob in the command" is exactly what the OP wanted to do... – Marcel Stimberg Aug 24 '10 at 13:31
  • It's informational not critical. – gvkv Aug 24 '10 at 13:48
  • 4
    No problem, I'm not afraid of criticism. It's just that I didn't understand the connection between the comment and my answer. But as I did also not understand your the connection between your answer and the original question that's consistent at least ;) – Marcel Stimberg Aug 24 '10 at 13:55
  • Yeah, I got caught up with trying to make extended globbing patterns work with . the way I think it should using ls as a canonical command and forgot about the details of the post. – gvkv Aug 24 '10 at 14:03
  • I should add, this is a great answer and you've solved more than one problem that I've had in the past and have resorted to hacks to make work. I never knew about GLOBIGNORE before now. BTW--edit your post in some inconsequential way. For some reason my up-vote is not showing and when I try to add it, I get a dialog telling me that my vote's locked in unless the post is edited. – gvkv Aug 24 '10 at 14:04
  • Thanks -- actually I found out about GLOBIGNORE only for this answer ;) I edited my post slightly, thanks in advance for voting. – Marcel Stimberg Aug 24 '10 at 14:28
  • @MarcelStimberg you can disable dotglob after setting GLOBIGNORE. that will then have the effect i want. please add that to your answer. – Lesmana Aug 24 '10 at 15:58
  • @lesmana you are completely right, I corrected my answer. Wonder why dotglob is automatically set in the first place... – Marcel Stimberg Aug 24 '10 at 16:53
  • @MarcelStimberg: because it's handy. Since GLOBIGNORE supercedes dotglob, by not including .* in GLOBIGNORE, bash presumes that you want to see dotfiles – MestreLion Mar 28 '13 at 07:43
  • Note that this does not work when prefixing the pattern with ./. – John B Jun 12 '16 at 00:08
  • Side note: . and .. are always ignored as long as GLOBIGNORE has a non-null value. GLOBIGNORE=:, for instance, would work for the example above. That's less readable, of course, so I note this only for players of America's Fastest Growing Quiz Game ®, Bash Trivia © – B Layer Aug 20 '20 at 16:47
31

The first glob below requires a leading dot and at least one additional non-dot character. These two globs together will match any possible hidden files, including files with more than one leading dot, but not . or .., which is exactly what you asked for.

ls -ld .[!.]* ..?*
bukzor
  • 487
  • 5
  • 5
9

With zsh, fish, pdksh, and at least some of its derivatives like mksh, posh, ksh93 since ksh93u+m 2020-08-09, bash since 5.2:

echo .*

(the globs of those shells never expand . or .., which is the most sensible thing to do).

With older versions of bash:

shopt -s dotglob
echo [.]*

(with dotglob, . and .. are not expanded unless you use a glob that starts with a literal . as in .* or dir/.*, as otherwise things like chmod +rwx -- * would have unexpected consequences).

Or:

GLOBIGNORE=.
echo .*

(with bash, like for dotglob and for the same reason, as soon as GLOBIGNORE is non-empty, both . and .. are automatically ignored in globs not contains slashes (and dotglob is enabled) and this time even with a literal . as above. Note that GLOBIGNORE=/ for instance would have the same effect).

Note however that . and .. would still be included in globs like dir/.* or .*/file.

With older versions of ksh93

FIGNORE='@(.|..)' # or FIGNORE=.:..
echo .*
9

Are you just looking for files? Are you in a position to use find?

Something like (assuming GNU find):

find . -mindepth 1 -maxdepth 1 -name ".*" -printf "%P\n"
Deano
  • 91
7

You want to show hidden files/folders BUT . and .. ?

Use this bash wildcard {.[!.]*,..?*}

Sample Data

You can try by generating sample data :

$ touch ...a  ...aa  ..a  ..aa  .a  .aa  .aaa  a
$ mkdir ...b  ...bb  ..b  ..bb  .b  .bb  .bbb  b

Proof

Below you can see the expected removed entries :

$ diff <(\ls -a) <(\ls -ad {.[!.]*,..?*})
1,3d0
< .
< ..
< a
11d7
< b

Pro

Useful to delete all hidden elements for example :

$ rm -rf .*
rm: cannot remove directory: `.'
rm: cannot remove directory: `..'

# <regenerate sample data here>

$ rm -rf {.[!.]*,..?*}
# No error
frntn
  • 358
  • 3
    with modern bash you can factor out * {.[!.],..?,}* – Pablo Marin-Garcia Jan 07 '20 at 17:59
  • 1
    what is the point in using braces expansion vs .[!.]* ..?* (just a space between two globs)? – maoizm Oct 11 '20 at 15:43
  • 1
    @maoizm If you want to specify files in a subfolder, then brace expansion makes more sense: folder/{.[!.],..?}/*. Note that the error-free rm is only a half-truth. Because the single semantik "all hidden files" is split into two different wildcards, it can result in errors if only one type of hidden folder exists: rm: cannot remove '.[!.]*': No such file or directory even though a file ...a did exist. – mxmlnkn Mar 29 '22 at 17:42
  • @mxmlnkn now I see the point, this is indeed a non-obvious corner case – maoizm Apr 04 '22 at 13:41
5

Try ls -A.  Here's an excerpt from the manual:

-A, --almost-all

    do not list implied . and ..
  • Plus 1, I was looking for how to hide the . and .. files from the ls ouput and stumbled on your answer. Thanks a lot Orso Grigio :) – Forever Learner May 09 '17 at 13:05
  • Not exactly an answer to the original question, as it also lists non-hidden files, but exactly what I was looking for! – Gerrit-K Oct 06 '18 at 09:37
-2
ls -1a|egrep -v '^(\.|\.\.)$'
codehead
  • 4,960