3

The command

    ls .*

when run gives as output the following :

  • All the files in the current directory starting with a . (hidden files)
  • All the files in the hidden directories present in the current directory
  • All the files in the current directory
  • All the files in the parent directory

Why does the command

    ls *.

not display :

  • All the files in the current directory
  • All the files in the parent directory

Reason I am thinking so is : The regular expression *. should match both . and .. So ls should be run on both and thus the output which I am expecting should be displayed

3 Answers 3

8

It's because * doesn't match files starting with a . by default. Consider the following directory:

$ ls -la 
total 8404
drwxrwxrwx   2 terdon terdon 8105984 Dec 31 13:14 .
drwxr-xr-x 153 terdon terdon  491520 Dec 30 22:32 ..
-rw-r--r--   1 terdon terdon       0 Dec 31 13:14 .dotfile
-rw-r--r--   1 terdon terdon       0 Dec 31 13:14 file1
-rw-r--r--   1 terdon terdon       0 Dec 31 13:14 file2
-rw-r--r--   1 terdon terdon       0 Dec 31 13:14 file3.

Let's see what each of the globs you used expands to:

$ echo .*
. .. .dotfile

$ echo *.
file3.

$ echo *
file1 file2 file3.

As you can see, the * does not include files or directories starting with . so both ./ and ../ are ignored. The same thing happens with your ls example. In bash, you can change this with the dotglob parameter, which will cause * to expand to dotfiles as well (but not to . or .., those are still ignored):

$ shopt -s dotglob
$ echo *
.dotfile

Other shells behave differently. For example zsh also ignores the . and .. when using .*:

% echo .*
.dotfile
1
  • @8bittree oh wow, yes indeed I did. Thanks!
    – terdon
    Feb 19, 2020 at 20:32
5

The rule for filename expansion have a special case for . as the first character in a filename: it must be explicitly matched (i.e. the pattern must contain a starting ., or . after a /). Otherwise these files are not candidates.

This is why your first version does pick up filenames that start with ., but the second doesn't. * doesn't match . as the first character of a filename.

POSIX Shell Command Language describes it as:

If a filename begins with a period ( '.' ), the period shall be explicitly matched by using a period as the first character of the pattern or immediately following a slash character. The leading period shall not be matched by:

  • The asterisk or question-mark special characters
  • A bracket expression containing a non-matching list, such as "[!a]", a range expression, such as "[%-0]", or a character class expression, such as "[[:punct:]]"

It is unspecified whether an explicit period in a bracket expression matching list, such as "[.abc]", can match a leading period in a filename.

Your shell might have options to change this behavior. Bash has this for instance (Filename expansion):

When a pattern is used for filename expansion, the character ‘.’ at the start of a filename or immediately following a slash must be matched explicitly, unless the shell option dotglob is set. When matching a file name, the slash character must always be matched explicitly. In other cases, the ‘.’ character is not treated specially.


Note that these are not regular expressions. .* as a regex would match anything at all (including nothing). *. would be ill-formed.

0

Note that this really has nothing to do with ls. The shell you are using does globbing (more generally, substitutions). The * or .* you give is expanded (globbed) by the shell to all object names matching * (everything that doesn't start ., a . at the beginning is a special case here) or .* (everything whose name starts .). That list is handed to ls (or whatever hapless command is being called), and that one now is in charge. Your commands never learn if they were invoked with * or if you painstakingly wrote out all names.

Rationale is that it simplifies hundreds (literally) of commands, and moreover makes their "argument handling" uniform (by not doing it at all ;-). Downside is that you can't say e.g. mv *.txt *.bak as you'd like to move all .txt files to .bak with the same name. Oh, well.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .