5

I'm trying to understand linux, its command-line and this quote:

You can run into problems with globs because .* matches . and .. (the current and parent directories). You may wish to use a pattern such as .[^.]* or .??* to get all dot files except the current and parent directories.

When exactly (in what command) would you use .[^.]* or .??*?

  • 1
    .??* would not match the file .X -- .??* matches a dot, then any character then another character then zero or more characters – glenn jackman Sep 15 '17 at 15:20
  • @glennjackman: the OP should also notice that .??* won't match the current or parent directories. However, it won't match files like .a or .P either. Those names are really uncommon, but they are valid. – Quora Feans Sep 15 '17 at 20:34
  • One recommendation if you want to match all dot files without duplicates, use both of these together: .[^.] and .??* The first matches all two-letter files starting with dot, but not dot-dot. The second matches all dot files of three characters or more. That combined with a * which matches all non-dot files will match all files inside the current folder. – penguin359 Sep 15 '17 at 20:37
  • The recommended solution by others of .[^.]* will fail to match files of 3 or more characters that start with dot-dot. Not that that's a common occurrence... – penguin359 Sep 15 '17 at 20:39
9

That's to work around a bug/misfeature in shells other than zsh, fish and the descendants of the Forsyth shell (including pdksh and derivatives), whereby the .* globs includes . and .. (on systems (most, unfortunately) where readdir() returns them)

With those shells,

chmod -R og-rwx .*

for instance would recursively remove rwx permissions to the current and parent directory instead of just the hidden files and directories in the current directory. A work around had to be added to rm for that as too many people tripped on rm -rf .*.

It's also (probably more often) used to pass all files (hidden or not) as arguments to a command (cmd .[!.]* ..?* *), for which you'll find other workarounds depending on the shell.

It's particularly bad for commands that do things recursively or act on directories like ls .*, chown -R .*, find .*, grep -r blah .* but it's still annoying for most other commands and I can't think of any commands for which you'd want to have those . and .. included in the list of files passed to them.

The .[^.]* glob (.[!.]* in Bourne/POSIX shells) excludes . (as it matches on filenames with at least two characters) and .. (as the second character is . which doesn't match [^.]), but also excludes files like ..foo, for which you need the second glob ..?*.

Those . and .. are tools for directory traversal, it's a mistake that they should be listed like ordinary files. POSIX requires them to be understood in path components (like in open("."), stat("foo/../bar")) but not necessarily be implemented as directory entries nor included in readdir().

Still, most systems still do implement those like in the early Unices as hard links, and those that don't will still fake entries for them in the output of getdents()/readdir().

With bash, an alternative is to turn the dotglob option on and use:

chmod -R og-rwx [.]*

(though beware that if there's no non-hidden file, it could change the permissions of the [.]* file unless you had the failglob option on to mimic the behaviour of zsh/fish).

As a history note, filename starting with . being hidden files were born from a coding mistake from someone trying to skip . and .. in the first place. It's ironical that when trying to do things with hidden files we would run into the same problem.

5

When you'd like to move all files and directories with hidden names (starting with a dot) to another location:

mv .[^.]* old-dot-files/

Or, whenever you'd like to do anything on all dot-files or dot-directories in a directory:

for name in .[^.]*; do
   # process "$name"
done

Note that the pattern .??* requires the matched names to have at least three characters, so names like .a will not be picked up. .[^.]* on the other hand will skip anything with a double dot at the start of the name.

It may be better to explicitly test the matched names:

for name in .*; do
    # expecting a regular file
    if [ -f "$name" ]; then
        # process "$name"
    fi
done

for name in .*; do
    # expecting a directory other than . and ..
    if [ -d "$name" ] && [ "$name" != '.' ] && [ "$name != '..' ]; then
        # process "$name"
    fi
done

In my experience, wanting to do something an all dot-file or dot-directories in a directory happens extremely seldom.

  • 2
    But .[^.]* would miss files named ..foo. It's odd that negative patterns like this are still a pain to this day. – Satō Katsura Sep 15 '17 at 15:20
  • @SatōKatsura That's very true. – Kusalananda Sep 15 '17 at 15:21
  • Interestingly, zsh .* ignores . and ... Yet another reason to use it :-) – Kevin Sep 15 '17 at 16:49
  • @Kevin This is also true for pdksh, but not in ksh93 by default. – Kusalananda Sep 15 '17 at 16:54

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.