I know using the command ls
will list all the directories. But what does the ls *
command do ? I used it and it just lists the directories. Does the star in front of ls
mean how deep it will list the directories?
5 Answers
ls
lists the files and content of directories it is being passed as arguments, and if no argument is given, it lists the current directory. It can also be passed a number of options that affect its behaviour (see man ls
for details).
If ls
is being passed an argument called *
, it will look for a file or directory called *
in the current directory and list it just like any other. ls
doesn't treat the *
character in any other way than any other one.
However if ls *
is a shell command line, that is code in the language of a Unix shell, then the shell will expand that *
according to its globbing (also referred to as Filename Generation or Filename/Pathname Expansion) rules.
While different shells support different globbing operators, most of them agree on the simplest one *
. *
as a pattern means any number of characters, so *
as a glob
will expand to the list of files in the current directories that match that pattern. There's an exception however that a leading dot (.
) character in a file name has to be matched explicitly, so *
actually expands to the list of files and directories not starting with .
(in lexical order).
For instance, if the current directory contains the files called .
, ..
, .foo
, -l
and foo bar
, *
will be expanded by the shell to two arguments to pass to ls
: -l
and foo bar
, so it will be as if you had typed:
ls -l "foo bar"
or
'ls' "-l" foo\ bar
Which are three ways to run exactly the same command. In all 3 cases, the ls
command (which will probably be executed from /bin/ls
from a lookup of directories mentioned in $PATH
) will be passed those 3 arguments: "ls", "-l" and "foo bar".
Incidentally, in this case, ls
will treat the first (strictly speaking second) one as an option.
Now, as I said, different shells have different globbing operators. A few decades ago, zsh
introduced the **/
operator¹ which means to match any level of subdirectories, short for (*/)#
and ***/
which is the same except that it follows symlinks while descending the directories.
A few years ago (July 2003, ksh93o+
), ksh93
decided to copy that behaviour but decided to make it optional, and only covered the **
case (not ***
). Also, while **
alone was not special in zsh
² (just meant the same as *
like in other traditional shells since **
means any number of character followed by any number of characters), in ksh93, **
meant the same as **/*
(so any file or directory below the current one (excluding hidden files)³.
bash
copied ksh93
a few years later (February 2009, bash 4.0), with the same syntax but an unfortunate difference: bash's **
was like zsh
's ***
, that is it was following symlinks when recursing into sub-directories which is generally not what you want it do and can have nasty side effects. It was partly fixed in bash-4.3 in that symlinks were still followed, but recursion stopped there. It was fully fixed in 5.0.
yash
added **
in version 2.0 in 2008, enabled with the extended-glob
option. Its implementation is closer to zsh
's in that **
alone is not special. In version 2.15 (2009), it added ***
like in zsh
and two of its own extensions: .**
and .***
to include hidden dirs when recursing (in zsh
, the D
glob qualifier (as in **/*(D)
) will consider hidden files and directories, but if you only want to traverse hidden dirs but not expand hidden files, you need ((*|.*)/)#*
or **/[^.]*(D)
).
fish also supports **
. Like earlier version of bash
, it follows symlinks when descending the directory tree. In that shell however **/*
is not the same as **
. **
is more an extension of *
that can span several directories. In fish
, **/*.c
will match a/b/c.c
but not a.c
, while a**.c
will match a.c
and ab/c/d.c
and zsh
's **/.*
for instance has to be written {,**/}.*
. There, ***
is understood as **
followed by *
so the same as **
.
tcsh
also added a globstar
option in V6.17.01 (May 2010) and supports both **
and ***
à la zsh
.
So in tcsh
, bash
and ksh93
, (when the corresponding option is enabled (globstar
)) or fish
, **
expands all the files and directories below the current one, and ***
is the same as **
for fish
, a symlink traversing **
for tcsh
with globstar
, and the same as *
in bash
and ksh93
(though it's not impossible that future versions of those shells will also traverse symlinks).
Above, you'll have noticed the need to make sure none of the expansions is interpreted as an options. For that, you'd do:
ls -- *
Or:
ls ./*
There are some commands (it doesn't matter for ls
) where the second is preferable since even with the --
some filenames may be treated specially. It's the case of -
for most text utilities, cd
and pushd
and filenames that contain the =
character for awk
for instance. Prepending ./
to all the arguments removes their special meaning (at least for the cases mentioned above).
It should also be noted that most shells have a number of options that affect the globbing behaviour (like whether dot files are ignored or not, the sorting order, what to do if there's no match...), see also the $FIGNORE
parameter in ksh
Also, in every shell but csh
, tcsh
, fish
and zsh
, if the globbing pattern doesn't match any file, the pattern is passed as an unexpanded argument which causes confusion and possibly bugs. For instance, if there's no non-hidden file in the current directory
ls *
Will actually call ls
with the two arguments ls
and *
. And as there's no file at all, so none called *
either, you'll see an error message from ls (not the shell) like: ls: cannot access *: No such file or directory
, which has been known to make people think that it was ls
that was actually expanding the globs.
The problem is even worse in cases like:
rm -- *.[ab]
If there's no *.a
nor *.b
file in the current directory, then you might end up deleting a file called *.[ab]
by mistake (csh
, tcsh
, and zsh
would report a no match error and wouldn't call rm
(and fish
doesn't support the [...]
wildcards)).
If you do want to pass a literal *
to ls
, you have to quote that *
character in some way as in ls \*
or ls '*'
or ls "*"
. In POSIX-like shells, globbing can be disabled altogether using set -o noglob
or set -f
(the latter not working in zsh
unless in sh
/ksh
emulation).
¹ While (*/)#
was always supported, it was first short-handed as ..../
in zsh-2.0 (and potentially before), then ****/
in 2.1 before getting its definitive form **/
in 2.2 (early 1992)
² The globstarshort
option, has since be added (in 2015) to allow **
and ***
being used instead of **/*
and ***/*
respectively
³ See also these few more oddities with the ksh93 globstar
design, some of which were copied by bash
..
-
7Another nice example is
find -name *
. Especially with more complex patterns, if there is exactly one match in the current directory, people will often not realize they're not passing the asterisk tofind
.– njsgCommented Jan 27, 2013 at 10:57 -
2In fish,
*.[ab]
would try to delete everything ending with.[ab]
.[
is not special in fish.– nullCommented Apr 17, 2014 at 9:26
The command ls
defaults to ls .
: List all entries in the current directory.
The command ls *
means 'run ls on the expansion of the *
shell pattern'
The *
pattern is processed by the shell, and expands to all entries in the current directory, except those that start with a .
. It will go one level deep.
The interpretation of double or triple *
patterns depend on the actual shell used.
*
is a wildcard that matches 0 or more characters. Some modern shells will recurse into subdirectories on seeing the **
pattern.
-
18Except that extra
*
do add something, see the other answer explaining the globstar. It should also be made clear that ls has nothing to do with the asterisks, it's never passed any of these asterisks at all. Runecho ls *
to see what would be executed when you writels *
.– njsgCommented Jan 26, 2013 at 21:49
You can demystify the whole process by typing echo
instead of ls
first, to see what the command expands to:
$ echo *
Applications Downloads Documents tmp.html
So in this case, ls *
expands to ls Applications Downloads Documents tmp.html
$ echo **
Applications Downloads Documents tmp.html
$ echo ***
Applications Downloads Documents tmp.html
So no change. This assumes you're using bash
as your shell -- most people are, and different shells have different behavior. If you're using ash
or csh
or ksh
or zsh
, you may expect things to work differently. That's the point of having different shells.
So lets try something different (still with bash
) so we get an idea of the the globbing (*
) operator can do for us. For example, we can filter by part of the name:
$ echo D*
Downloads Documents
And interestingly, a trailing slash is an implicitly part of any directory name. So */
will yield only the directories (and symbolic links to directories):
$ echo */
Applications/ Downloads/ Documents/
And we can do some filtering at multiple levels by putting slashes in the middle:
$ echo D*/*/
Documents/Work/ /Documents/unfinished/
Since the Downloads
directory doesn't contain any subdirectories, it does not end up in the output. This is very useful for just examining the files you want. I use commands like this all the time:
$ ls -l /home/*/public_html/wp-config.php
This lists, if there are any, all the wp-config.php
files that exist at the base level of any user's public_html
directory. Or perhaps to be more complete:
$ find /home/*/public_html/ -name wp-config.php
This will find any wp-config.php
files in any user's public_html
directories or any of their subdirectories, but it will operate more efficiently than just find /home/ -name wp-config.php
because it won't examine anything but the public_html
directories for each of the users.
-
2"This assumes you're using
bash
as your shell" ← and that the globstar is not enabled.shopt -s globstar
and try it again...– njsgCommented Jan 27, 2013 at 17:53 -
1Another way to demystify is
set -x
which will start printing the "actual" command executed each time (turn off withset +x
). Commented Sep 20, 2018 at 20:58
In some shells, including bash 4.x with the globstar
option enabled, **
will perform a recursive glob, descending matched directories. Additional asterisks don't further modify this operation.
-
1As I said in my answer though, beware that contrary to
ksh93
andzsh
,bash
does traverse symlinks in the recursion which is generally unwanted. Commented Jan 26, 2013 at 21:12 -
If you want to "dive deep", use ls -R (recursive) option, or use find, like so:
find . -ls
"find" will dive down to the bottom of the directory tree (as will 'ls -R'), and has many more options, like listing directories (-type d), files only (-type f) or showing files having other characteristics (no user in /etc/passwd, specific permissions, and a whole lot more). "find" is also somewhat safer in scripting (due to inconsistent globbing rules between shells, as well as special escapes for files having dashes, etc).
shell wildcard globbing won't work with just an asterisk '*' on dotfiles. To list dotfiles only, use:
ls .??*
-
I never understood why if
ls -R
gives results containing foo.xml,ls -R *.xml
doesn't. At least that's true for macos's ls. Any clues? Commented Feb 23, 2020 at 10:05 -
@SteakOverflow If the
foo.xml
is not in the directory it won't find it because it's not in the directory but a subdirectory. That's why.ls -R
recurses into subdirectories so it shows the files found but when runningls -R *.xml
it's trying to recurse through directories in the current directory - but it doesn't exist in the current directory but rather a subdirectory so it can't search it. I hope that's clear. I'm exhausted.– PryftanCommented Apr 27, 2023 at 16:49