30

I want to carry out some action (say chown) on all the hidden files in a directory.

I know that this .* is not a good idea because it will also find the current . and parent .. directories (I know that rm will fail to operate on . and .. but other commands, including chown and chmod, will happily take effect)

But all my hidden files have different names!

How should I glob for all hidden files while excluding . and .. ?

Zanna
  • 69,223
  • 56
  • 216
  • 327

4 Answers4

30

In Bash, set the GLOBIGNORE variable as follows:

GLOBIGNORE=".:.."

to hide the . and .. directories. This also sets the dotglob option so that the glob * character now matches both hidden and non-hidden files. Setting the GLOBIGNORE variable as shown above only affects the current terminal session and top-level processes unless you export it and and add it to your ~/.bashrc file as export GLOBIGNORE=".:..".

You can also do:

shopt -s dotglob

Source: Gilles' answer here :)

Gabriel Staples
  • 8,025
  • 7
  • 66
  • 105
Rinzwind
  • 293,910
  • 41
  • 570
  • 710
  • 3
    IMO this is better than the accepted answer. Easier to remember, infinitely more intuitive, and able to be stuffed into `.bashrc` to make it permanent if one so wishes (I know I almost never want `*` to mean "non-dotfiles only"). – jcgoble3 Sep 27 '21 at 04:30
22

You can use the following extglob pattern:

.@(!(.|))
  • . matches a literal . at first

  • @() is a extglob pattern, will match one of the patterns inside, as we have only one pattern inside it, it will pick that

  • !(.|) is another extglob pattern (nested), which matches any file with no or one .; As we have matched . at start already, this whole pattern will match all files starting with . except . and ...

extglob is enabled on interactive sessions of bash by default in Ubuntu. If not, enable it first:

shopt -s extglob

Example:

$ echo .@(!(.|))
.bar .foo .spam
heemayl
  • 90,425
  • 20
  • 200
  • 267
  • 1
    This is clearly an awesome thing I need to learn about! Thank you for teaching – Zanna Sep 26 '16 at 10:38
  • @Zanna Glad i could help :) – heemayl Sep 26 '16 at 10:39
  • 2
    What is the purpose of the `@()`? Simple `.!(.|)` seems to work identically. – Kyle Strand Sep 26 '16 at 16:49
  • 1
    I know that this is old, but I have the same question as @KyleStrand. In my tests, `!(.|)` works the same. Is there any purpose behind `@()` in this context? – Paddy Landau Apr 02 '17 at 07:26
  • Is the bang character a negation? It is not mentioned, and reading your explanation, I get the impression, that the pattern matches dot and doubledot, but you clearly describe files which match a starting dot, except just those two. – user unknown May 14 '19 at 21:21
  • 1
    @userunknown, right, `!()` means to not match any of the pipe-separated alternatives inside of it, so basically this is saying: match a dot, but do not let the second character match a dot or an empty string. As pointed out above, the `@()` which means to pick only one of the pipe-separated alternatives, does not have a purpose since there are no alternatives. My golden source for glob information is this primer from node-glob (See https://github.com/isaacs/node-glob#glob-primer) which is equivalent to enabling dotglob in the shell – Miguel Sánchez Villafán Jan 29 '23 at 14:47
10

You can use a find command here. For example something like

find -type f -name ".*" -exec chmod 775 {} \;

This will find hidden files and change permissions


Edit to include the comment by @gerrit:

find -type f -maxdepth 1 -name ".*" -exec chmod 775 {} \;

This will limit the search top the current directory instead of searching recursively.

Wayne_Yux
  • 4,873
  • 3
  • 27
  • 35
2

In case anyone need an alternative that doesn't require environment variables or shell options enabled like dotglob or extglobyou can use the following:

.[!.]*

it works because it matches at least two characters, the second of which must not be a dot (.)

I came to this question because I was looking for a way to match dotfiles and non-dotfiles, for which you could use:

{.[!.]*,*}

which matches either of the two options.

I needed a glob that didn't require environment variables nor shell options; and the reason is that I needed a glob that worked in VSCode's findFiles API, which supports globs (See https://code.visualstudio.com/api/references/vscode-api#3906), but doesn't offer room for shell options nor dotglob or extglob equivalents. It is nice that these tools decided to use the *NIX way for doing globs.