I have a directory that contains thousands of files, some of them are hidden.

The command ls -a list all files, including hidden ones, but I need just to list hidden files.

What command should I use?

  • 8
    A simple ls -ld .* or ls -ald .* will work May 1, 2018 at 15:11

23 Answers 23


The command :

ls -ld .?* 

Will only list hidden files .

Explain :

 -l     use a long listing format

 -d, --directory
              list  directory entries instead of contents, and do not derefer‐
              ence symbolic links

.?* will only state hidden files 
  • 9
    You don't need the two question marks in there, the * covers it ( ? only matches any single character, * matches any number of them ).
    – psusi
    May 19, 2014 at 0:50
  • 17
    @psusi, I think the intent is to exclude . and .. from the match. However it will also exclude (perfectly legal) single-character hidden filenames such as .a, .1 and so on. Perhaps a better extended glob would be .!(|.) i.e. literal dot followed by anything except nothing or another (single) dot i.e. ls -d .!(|.) May 19, 2014 at 1:25
  • @steeldriver, neat, the ?? version does exclude "." and "..". This seems to be the result of an interesting quirk: neither ? nor * will match a dot, but the ? must match something otherwise the name is ignored.
    – psusi
    May 19, 2014 at 3:37
  • To identify directories and files add the F option, i.e., ls -ldF .?* directory names have "/" as last displayed character files don't.
    – RCF
    May 19, 2014 at 21:30
  • 1
    This almost works except it also list hidden folder like .vim, which I consider not a file here. I modify it a bit like ls -ldp .?* | grep -v / This only list hidden files not hidden folder
    – Fred Yang
    Jan 26, 2018 at 5:33
ls -d .!(|.)

Does exactly what OP is looking for .

  • 1
    How does that work? Jun 7, 2018 at 17:56
  • list directory anything with a . and do not list anything without one is what it pretty much translates into.
    – patrick
    Jun 12, 2018 at 15:22
  • 1
    I understand that in !(pattern-list), pattern-list is a list of one or more patterns separated by a |, but it is confusing for me that you don't have any pattern to the left of |. Could you please explain that syntax to me? Sep 18, 2018 at 13:35
  • 2
    @CarlosMendoza that would become the empty pattern, so this is like . not followed by (nothing or .), excluding . itself and ...
    – muru
    Sep 26, 2018 at 2:27
  • Don't forget to set extglob before using this solution! It may not be enabled by default. shopt -s extglob
    – user77376
    Apr 28, 2021 at 15:00
ls -ad .*

works for me in Bash.

  • Shows . and .. in Bash Aug 29, 2018 at 14:09
  • @PengheGeng is correct ls -ad .* shows . and .. - but ls -ad .?* shows .. Sep 29, 2021 at 21:57

If you just want the files in your current directory (no recursion), you could do

echo .[^.]*

That will print the names of all files whose name starts with a . and is followed by one or more non-dot characters. Note that this will fail for files whose name starts with consecutive dots, so for example ....foo will not be shown.

You could also use find:

find -mindepth 1 -prune -name '.*'

The -mindepth ensures we don't match . and the -prune means that find won't descend into subdirectories.


Using find and awk,

find . -type f | awk -F"/" '$NF ~ /^\..*$/ {print $NF}'


find . -type f --> List all the files in the current directory along with it's path like,


awk -F"/" '$NF ~ /^\..*$/ {print $NF}'

/ as field separator awk checks for the last field staring with a dot or not. If it starts with a dot, then it prints the last field of that corresponding line.

  • You could just use find -type f. You don't need to explicitly set the search path or -name "*".
    – terdon
    May 19, 2014 at 3:08

find is usually a better option for complicated searches than using name globbing.

find . -mindepth 1 -maxdepth 1 -name '.*'


find . -mindepth 1 -maxdepth 1 -name '.*' -o -name '*~'

find . searches current directory

-mindepth 1 excludes . and .. from the list

-maxdepth 1 limits the search to the current directory

-name '.*' find file names that start with a dot

-o or

-name '*~' find file names that end with a tilde (usually, these are backup files from text editing programs)

However, this and all of the other answers miss files that are in the current directory's .hidden file. If you are writing a script, then these lines will read the .hidden file and display the file names of those that exist.

if [[ -f .hidden]] # if '.hidden' exists and is a file
    while read filename # read file name from line
        if [[ -e "$filename" ]] # if the read file name exists
            echo "$filename" # print it
    done < .hidden # read from .hidden file
  • What's the .hidden file? Why would there ever be a file called .hidden that contains the file names? Anyway, if there is one why would you do something that complex when all you would need would be cat .hidden? Your find command is correct(ish) but the -name '*~' is irrelevant. Files that end in tildes are backup files but not hidden in any way.
    – terdon
    May 21, 2014 at 3:19
  • @terdon The .hidden file is for files and folders you want to hide when you can't change the file/folder name to start with a dot. As for files that end in tildes, it depends on the system. ls -B will ignore such files, as will most GUI file explorers.
    – Mark H
    May 2, 2016 at 21:31
  • cat .hidden may show files that no longer exist if those files were deleted or moved since being added to the .hidden file.
    – Mark H
    May 2, 2016 at 21:35
  • This works well. Aug 29, 2018 at 14:10

I think that you can do it with following command.

ls -a | grep "^\." | grep -v "^\.$" | grep -v "^\..$"

ls -a command you entered, that shows all files and directories in current working directory.

grep "^\." command I appended, that filters output to shows only hidden files(It's name starts with ".").

grep -v "^\.$" | grep -v "^\..$" command I appended, that filters output to exclude ., ..(They are current and parent directory).

If some filenames can have more than a line with "\n", above example could be incorrect.

So I suggest following command to solve it issue.

find -maxdepth 1 -name ".[!.]*"

What else you could have done, is ls .?* Or ls .!(|) that will show you everything in the current dir hidden files/dirs on the top and other files/dirs below

e.g: from my terminal

$ ls .?*       
.bash_history    .dmrc        .macromedia   .weather
.bash_logout     .gksu.lock   .profile      .wgetrc
.bash_profile    .bashrc.save .ICEauthority .toprc           .Xauthority
.bashrc          .lastdir     .viminfo      .xsession-errors
.bashrc~         .dircolors   .lynxrc       .vimrc           .xsession-errors.old



cache  config

compizconfig-1                              rhythmbox
dconf                                       shotwell

Now notice in the above results, it shows you every file/dir with its subdir and any hidden files right below.

$ ls .!(|)
.bash_aliases  .bashrc1  .bashrc1~

askapache-bash-profile.txt  examples.desktop             Public           top-1m.csv
backups             Firefox_wallpaper.png        PycharmProjects          top-1m.csv.zip
Desktop             java_error_in_PYCHARM_17581.log  Shotwell Import Log.txt  topsites.txt
Documents           Music                Templates            Videos
Downloads           Pictures                 texput.log           vmware

Sorry, I cannot comment. to explain the difference here between ls .?* and @cioby23 answer ls -d .[!.]* .??* And why it is actually printing hidden files twice is because literally you're asking twice .??*, .?*, .[!.]* they're the same thing, so adding any of them with different command characters will print twice.


Approach 1: ls -d .{[!.],.?}*

Explain: I want to exclude . and .. but include file such as ..i_am_also_a_hidden_file.txt

  1. ls -d .* undesirably shows . and ..
  2. ls -d .?* (the current accepted answer) undesirably shows ..
  3. ls -d .[!.]* undesirably will not show ..i_am_also_a_hidden_file.txt
  4. ls -d .{[!.],.?}* is the answer

Approach 2: ls -A | grep "\\."

I personally like this way better


You can also use:

ls -d .[!.]* .??*

This will allow you to display normal hidden files and hidden files which begin with 2 or 3 dots for example : ..hidden_file

  • 1
    With this I get all hidden files twice.
    – TuKsn
    May 19, 2014 at 9:02

you can use the command

ls -Ad .??*

This has the advantage of allowing multi-column listing, unlike the grep-based approach in the ls -a | grep "^\." solutions


With bash, setting the GLOBIGNORE special variable is some non-empty value is enough to make it ignore . and .. when expanding globs. From the Bash docs:

The GLOBIGNORE shell variable may be used to restrict the set of filenames matching a pattern. If GLOBIGNORE is set, each matching filename that also matches one of the patterns in GLOBIGNORE is removed from the list of matches. If the nocaseglob option is set, the matching against the patterns in GLOBIGNORE is performed without regard to case. The filenames . and .. are always ignored when GLOBIGNORE is set and not null. However, setting GLOBIGNORE to a non-null value has the effect of enabling the dotglob shell option, so all other filenames beginning with a ‘.’ will match.

If we set it to .:.., both . and .. will be ignored. Since setting it to anything non-null will also get this behaviour, we might as well set it to just .


ls -d .*

All the answers so far are based on the fact that files (or directories) which names start with a dot are "hidden". I came up with another solution, that might not be as efficient, but this solution does not assume anything about the names of the hidden files, and therefore avoids listing .. in the result (as does the currently accepted answer).

The full command is:

ls -d $(echo -e "$(\ls)\n$(\ls -A)" | sort | uniq -u)


What this does is list all the files (and directories) twice,

echo -e "$(\ls)\n$(\ls -A)"

but only showing hidden files once -A.

Then the list is sorted | sort which makes regular (unhidden) files appear twice and next to each other.

Then, remove all lines that appear more than once | uniq -u, only leaving unique lines.

Finally use ls again to list all the files with the user's custom options and without listing the contents of the directories in the list -d.

EDIT (Limitations):

As muru pointed out, this solution will not work correctly if there are files with names such as escaped\ncharacter.txt because echo -e will split the filename into two lines. It will also not work as expected if there are two hidden files with almost the same name except for a special character, such as .foo[tab].txt and .foo[new-line].txt as both of those are printed as .foo?.txt and would be eliminated by uniq -u

  • 1
    I think that's pretty much the definition of hidden files. Even the origin of these hidden files is from a bug in dealing with . as the first character.
    – muru
    Sep 26, 2018 at 2:05
  • I mention that (as fact) in the first paragraph. It still however gets a different result than the current accepted answer
    – alejandro
    Sep 26, 2018 at 2:13
  • Sure, it excludes .., but I wonder how well it works with filenames containing special characters.
    – muru
    Sep 26, 2018 at 2:15
  • I don't know exactly what you mean by special characters, but I would think it would not pose a problem, as this solution does not assume anything about the filenames
    – alejandro
    Sep 26, 2018 at 2:21
  • Actually, you do assume at least one thing: filenames don't have newlines in them. Also: filenames don't have escape sequences in them that echo -e will interpret. Also: ls won't mangle filenames so that two different filenames get displayed the same (for example, in some versions ls will use ? for special characters, so .foo\tbar (\t => tab) and .foo\nbar (\n => newline) will both show up as foo?bar).
    – muru
    Sep 26, 2018 at 2:25

Alternatively, you can also use echo .*


user@linux:~$ echo .*
. .. .bash_aliases .bash_history .bash_logout .bashrc

If you prefer it in long list format, just convert the white space to a new line.

user@linux:~$ echo .* | tr ' ' '\n'

To list all hidden files and folders:

ls -ld .*

To list hidden files only:

ls -ld .*|grep -v ^d

To list hidden folders only:

ls -ld .*|grep ^d

You can also use process substitution

 diff <(ls -1A) <(ls -1)


Piping the stdout of a command into the stdin of another is a powerful technique. But, what if you need to pipe the stdout of multiple commands? This is where process substitution comes in. Process substitution feeds the output of a process (or processes) into the stdin of another process

Cited from here

So you are piping two outputs into the diff command.

The first command is "ls -1A". The "-A" flag works like the "-a" flag, but it does not show you "." and "..". So it basically shows you all the files and directories (both hidden and not-hidden) in a directory. The "-1" flag displays the output of "ls" one below the other vertically and not in a horizontal list.


I don't like -d because it only shows no matches found if there are no matches.

It also does not show total [size] at top of lines.

I think ls -lA --ignore "[^\.]*" is a better solution.


ls -A | grep "^\."

  • ls -A lists all files (hidden and non-hidden)
  • grep "^\." filter apart the ones starting with a dot
  • ls -a will also list all the files: hidden and non-hidden Nov 24, 2021 at 14:36

Thanks for all the tips on here!

Here's a function for your .bash_aliases file that handles no command arguments, doesn't throw errors on directories without hidden files, appends / on listed directories, and outputs to a single column.

lsa () {

    # Exit codes with/without argument are 0 if no hidden files present
    EXIT=$(ls -1d .!(|.) |& grep -q "No such file"; echo $?)
    EXIT_ARG=$(cd $1; ls -1d .!(|.) |& grep -q "No such file"; echo $?)

    # If no argument
    if [ $# -eq 0 ]; then
        if [ $EXIT -eq 0 ]; then
            printf ""
            ls -1dp .!(|.)
    # If argument
        if [ $EXIT_ARG -eq 0 ]; then
            printf ""
            (cd $1; ls -1dp .!(|.))


A long list of answers, let's add one more, which doesn't seem to be included:

$ ls -lA | grep ' \.'

The -A option to ls includes all hidden files, except . and ...
grep, as used above finds . (space, dot) and filters away lines that do not have that.

$ ls -lA | grep -E '^d.* \.
... lists only dirs.

$ ls -lA | grep -Ev '^d.*' | grep ' \.'
... lists only files.

... and that last can be used as alternative for files-only
$ ls -lA | grep -E '^d.*' | grep ' \.'
just remove the -v flag on grep


You can use the command

ls -pa | grep -v /

Explanation of flags:

  • -p will append "/" indicator to directories
  • -a will list hidden files
  • grep -v / commands will return the lines that do not contain a slash.

You could also install the much friendlier Rust version of ls: exa. Then use the following to get a much nicer tree visualization of all the files:

exa -Ta

If you really end up liking exa like me, you can override ls with an alias on your .bashrc: alias ls="exa".


You can use grep:

ls -a | grep '^\.'
  • This answer doesn't only show hidden files as the question requested; it shows hidden directories too.
    – karel
    Nov 25, 2021 at 7:21

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.