1066

This command lists directories in the current path: ls -d */

What exactly does the pattern */ do?

And how can we give the absolute path in the above command (e.g. ls -d /home/alice/Documents) for listing only directories in that path?

jww
  • 83,594
  • 69
  • 338
  • 732
Sibi
  • 43,989
  • 14
  • 80
  • 146

26 Answers26

1113

*/ is a pattern that matches all of the subdirectories in the current directory (* would match all files and subdirectories; the / restricts it to directories). Similarly, to list all subdirectories under /home/alice/Documents, use ls -d /home/alice/Documents/*/

Gordon Davisson
  • 95,980
  • 14
  • 99
  • 125
  • 96
    Note that `*/` won't match any hidden folders. To include them, either specify them explicitly like `ls -d .*/ */`, or [set the `dotglob` option](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html#The-Shopt-Builtin) in Bash. – Gergő Jan 11 '15 at 15:15
  • 1
    @GordonDavisson The message of failure could be avoided with bash option `shopt -s nullglob` –  May 16 '16 at 18:57
  • 5
    @BinaryZebra Setting nullglob would avoid the error message, but would cause even more confusion: with nullglob set and no subdirectories, `ls */` would expand to just `ls`, which would list all files in the current directory. I'm pretty sure that's not what you want in that case. – Gordon Davisson May 16 '16 at 19:24
  • 1
    `*/` also sets the names of the subdirs to be displayed with a trailing slash. – Titou Jan 06 '17 at 10:17
  • 1
    List all folders exclude `./` `../`: `ls -d */ .[^.]*/` – zhy Apr 03 '18 at 03:28
  • [This answer on the Unix & Linux SE](https://unix.stackexchange.com/questions/576589/where-can-i-find-a-definitive-answer-for-what-means-in-bash-or-zsh/576597#576597) details the POSIX standard for this. – Gordon Davisson Mar 29 '20 at 18:10
  • In this case ls prints filenames differently - in full format. Had to trim it to base name `ls -d /$HOM$/*/ | xargs -L1 basename` – user3132194 Apr 07 '20 at 09:12
527

Four ways to get this done, each with a different output format

1. Using echo

Example: echo */, echo */*/
Here is what I got:

cs/ draft/ files/ hacks/ masters/ static/  
cs/code/ files/images/ static/images/ static/stylesheets/  

2. Using ls only

Example: ls -d */
Here is exactly what I got:

cs/     files/      masters/  
draft/  hacks/      static/  

Or as list (with detail info): ls -dl */

3. Using ls and grep

Example: ls -l | grep "^d" Here is what I got:

drwxr-xr-x  24 h  staff     816 Jun  8 10:55 cs  
drwxr-xr-x   6 h  staff     204 Jun  8 10:55 draft  
drwxr-xr-x   9 h  staff     306 Jun  8 10:55 files  
drwxr-xr-x   2 h  staff      68 Jun  9 13:19 hacks  
drwxr-xr-x   6 h  staff     204 Jun  8 10:55 masters  
drwxr-xr-x   4 h  staff     136 Jun  8 10:55 static  

4. Bash Script (Not recommended for filename containing spaces)

Example: for i in $(ls -d */); do echo ${i%%/}; done
Here is what I got:

cs  
draft  
files  
hacks  
masters  
static

If you like to have '/' as ending character, the command will be: for i in $(ls -d */); do echo ${i}; done

cs/  
draft/  
files/  
hacks/  
masters/  
static/
Iulian Onofrei
  • 7,489
  • 8
  • 59
  • 96
Albert
  • 9,151
  • 4
  • 19
  • 27
  • 2
    **NB.** `3` was noticeably slower than the others for me. In the directory I tested: `1` .012s, `2` .016s, `3` **.055s**, `4` .021s. – isomorphismes Jul 30 '13 at 01:35
  • @isomorphismes, how did you measure the time? By what means? By the way, I have to say, No. `1` is my favorite. – Albert Aug 03 '13 at 01:47
  • Albert, I used `time`, the Unix function. /// I agree, `echo */` is cleverer than `ls -d */`. – isomorphismes Aug 05 '13 at 15:43
  • 9
    Two minor things that you should note. ls -l | grep "^d" filters out symbolic links to directories, whereas 'ls -d */' displays them. Also, 'ls -d */' fails if there are no directories present at all in the target directory. – Zoran Pavlovic Jan 06 '14 at 14:28
  • `ll -d */` worked beautifully for me, thanks for distilling an overabundance of man pages for that insightful tip. – Eric L. Mar 17 '14 at 16:36
  • 16
    The `1.` option will cause major pains when dealing with folders names that have spaces. – Shrikant Sharat May 08 '14 at 10:13
  • @BinaryZebra Will this fix be OK? `printf "%s\n" */ | tr -d "/"`. – Albert Jul 25 '15 at 06:16
  • ls -dl */ is a bit simpler than ls -l | grep "^d" – Iggy Jan 04 '18 at 12:34
  • For me, this command worked wonderfully (thanks for the idea): `for i in $(ls -d */); do printf ${i%%/} && printf " "; done` instead of newlines, it will leave blank spaces between folders (can be modified with other printfs). – John Hamilton Jun 05 '18 at 06:19
109

I use:

ls -d */ | cut -f1 -d'/'

This creates a single column without a trailing slash - useful in scripts.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Thomas Altfather Good
  • 1,201
  • 1
  • 8
  • 4
  • 21
    The `-1` option for `ls` will output in single-column format as well, as in `ls -1 -d */`. – Kalin May 01 '14 at 17:11
  • 3
    @user29020 Should note that `-1` still outputs a trailing slash for every entry. – Dennis Oct 22 '14 at 15:11
  • When I add this to `.bashrc` as alias, how do I add `'/'`? – RNA Nov 21 '14 at 08:55
  • @RNA late to answer, but for future people: try escaping with a backslash? `'\/'` – Caek Mar 02 '15 at 03:19
  • 1
    Breaks if there's a leading slash. This removes trailing slashes only: `| sed -e 's-/$--'` – Victor Sergienko Oct 23 '18 at 20:05
  • @Kalin I used to support the -1, but in reality you don't need it. Even if you see the directories aligned next to each other in a single line, if you count the number of lines with `wc -l` you will see that the result is number of directories. – thanos.a May 27 '21 at 06:57
  • This is the simplest solution to get a vertical list of the directories, without the forward slashes `/`. – thanos.a May 27 '21 at 06:59
66

For all folders without subfolders:

find /home/alice/Documents -maxdepth 1 -type d

For all folders with subfolders:

find /home/alice/Documents -type d
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
at3m
  • 661
  • 5
  • 2
  • 4
    To match the question (but include dotfiles), the first should be: `find /home/alice/Documents -maxdepth 1 -mindepth 1 -type d` otherwise you include `/home/alice/Documents` itself. To include symlinks to directories, prefix with `-L` – jhnc Feb 20 '19 at 22:27
  • This seems like the sanest solutions, since find is the tool that is actually made for this task. You can exclude the first result - ./ for the current directory - by piping to grep -v '^\./$' – siliconrockstar Mar 25 '19 at 22:40
41

Four (more) Reliable Options.

An unquoted asterisk * will be interpreted as a pattern (glob) by the shell. The shell will use it in pathname expansion. It will then generate a list of filenames that match the pattern.

A simple asterisk will match all filenames in the PWD (present working directory). A more complex pattern as */ will match all filenames that end in /. Thus, all directories. That is why the command:

1.- echo.

echo */
echo ./*/              ### Avoid misinterpreting filenames like "-e dir"

will be expanded (by the shell) to echo all directories in the PWD.


To test this: Create a directory (mkdir) named like test-dir, and cd into it:

mkdir test-dir; cd test-dir

Create some directories:

mkdir {cs,files,masters,draft,static}   # Safe directories.
mkdir {*,-,--,-v\ var,-h,-n,dir\ with\ spaces}  # Some a bit less secure.
touch -- 'file with spaces' '-a' '-l' 'filename'    # And some files:

The command echo ./*/ will remain reliable even with odd named files:

./--/ ./-/ ./*/ ./cs/ ./dir with spaces/ ./draft/ ./files/ ./-h/
./masters/ ./-n/ ./static/ ./-v var/

But the spaces in filenames make reading a bit confusing.


If instead of echo, we use ls. The shell is still what is expanding the list of filenames. The shell is the reason to get a list of directories in the PWD. The -d option to ls makes it list the present directory entry instead of the contents of each directory (as presented by default).

ls -d */

However, this command is (somewhat) less reliable. It will fail with the odd named files listed above. It will choke with several names. You need to erase one by one till you find the ones with problems.

2.- ls

The GNU ls will accept the "end of options" (--) key.

ls -d ./*/                     ### More reliable BSD ls
ls -d -- */                    ### More reliable GNU ls

3.-printf

To list each directory in its own line (in one column, similar to ls -1), use:

$ printf "%s\n" */        ### Correct even with "-", spaces or newlines.

And, even better, we could remove the trailing /:

$ set -- */; printf "%s\n" "${@%/}"        ### Correct with spaces and newlines.

An attempt like

$ for i in $(ls -d */); do echo ${i%%/}; done

will fail on:

  • some names (ls -d */) as already shown above.
  • will be affected by the value of IFS.
  • will split names on spaces and tabs (with default IFS).
  • each newline in the name will start a new echo command.

4.- Function

Finally, using the argument list inside a function will not affect the arguments list of the present running shell. Simply

$ listdirs(){ set -- */; printf "%s\n" "${@%/}"; }
$ listdirs

presents this list:

--
-
*
cs
dir with spaces
draft
files
-h
masters
-n
static
-v var

These options are safe with several types of odd filenames.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
  • I think you should replace the word Safe with Reliable. Your post makes me think that the other solutions are 'unsafe' (vulnerable or exploitable.) – Amado Martinez May 16 '16 at 03:03
  • 1
    @AmadoMartinez Done a general s/safe/reliable/. Better? –  May 16 '16 at 18:48
25

The tree command is also pretty useful here. By default it will show all files and directories to a complete depth, with some ASCII characters showing the directory tree.

$ tree
.
├── config.dat
├── data
│   ├── data1.bin
│   ├── data2.inf
│   └── sql
|   │   └── data3.sql
├── images
│   ├── background.jpg
│   ├── icon.gif
│   └── logo.jpg
├── program.exe
└── readme.txt

But if we wanted to get just the directories, without the ASCII tree, and with the full path from the current directory, you could do:

$ tree -dfi
.
./data
./data/sql
./images

The arguments being:

-d     List directories only.
-f     Prints the full path prefix for each file.
-i     Makes tree not print the indentation lines, useful when used in conjunction with the -f option.

And if you then want the absolute path, you could start by specifying the full path to the current directory:

$ tree -dfi "$(pwd)"
/home/alice/Documents
/home/alice/Documents/data
/home/alice/Documents/data/sql
/home/alice/Documents/images

And to limit the number of subdirectories, you can set the max level of subdirectories with -L level, e.g.:

$ tree -dfi -L 1 "$(pwd)"
/home/alice/Documents
/home/alice/Documents/data
/home/alice/Documents/images

More arguments can be seen with man tree.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Hamish Downer
  • 15,325
  • 14
  • 82
  • 80
18

In case you're wondering why output from 'ls -d */' gives you two trailing slashes, like:

[prompt]$ ls -d */
app//  cgi-bin//  lib//        pub//

it's probably because somewhere your shell or session configuration files alias the ls command to a version of ls that includes the -F flag. That flag appends a character to each output name (that's not a plain file) indicating the kind of thing it is. So one slash is from matching the pattern '*/', and the other slash is the appended type indicator.

To get rid of this issue, you could of course define a different alias for ls. However, to temporarily not invoke the alias, you can prepend the command with backslash:

\ls -d */
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
gwideman
  • 2,391
  • 1
  • 20
  • 37
13

A plain list of the current directory, it'd be:

ls -1d */

If you want it sorted and clean:

ls -1d */ | cut -c 1- | rev | cut -c 2- | rev | sort

Remember: capitalized characters have different behavior in the sort

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
PYK
  • 2,267
  • 18
  • 13
12

I just add this to my .bashrc file (you could also just type it on the command line if you only need/want it for one session):

alias lsd='ls -ld */'

Then lsd will produce the desired result.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
12

Actual ls solution, including symlinks to directories

Many answers here don't actually use ls (or only use it in the trivial sense of ls -d, while using wildcards for the actual subdirectory matching. A true ls solution is useful, since it allows the use of ls options for sorting order, etc.

Excluding symlinks

One solution using ls has been given, but it does something different from the other solutions in that it excludes symlinks to directories:

ls -l | grep '^d'

(possibly piping through sed or awk to isolate the file names)

Including symlinks

In the (probably more common) case that symlinks to directories should be included, we can use the -p option of ls, which makes it append a slash character to names of directories (including symlinked ones):

ls -1p | grep '/$'

or, getting rid of the trailing slashes:

ls -1p | grep '/$' | sed 's/\/$//'

We can add options to ls as needed (if a long listing is used, the -1 is no longer required).

Note: if we want trailing slashes, but don't want them highlighted by grep, we can hackishly remove the highlighting by making the actual matched portion of the line empty:

ls -1p | grep -P '(?=/$)'
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
pyrocrasty
  • 557
  • 6
  • 11
9

If a hidden directory is not needed to be listed, I offer:

ls -l | grep "^d" | awk -F" " '{print $9}'

And if hidden directories are needed to be listed, use:

ls -Al | grep "^d" | awk -F" " '{print $9}'

Or

find -maxdepth 1 -type d | awk -F"./" '{print $2}'
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
PHP Learner
  • 481
  • 4
  • 14
7

To show folder lists without /:

ls -d */|sed 's|[/]||g'
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
wholanda
  • 71
  • 1
  • 1
7

Try this one. It works for all Linux distribution.

ls -ltr | grep drw
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Raj RD
  • 75
  • 1
  • 2
7

For listing only directories:

ls -l | grep ^d

For listing only files:

ls -l | grep -v ^d 

Or also you can do as:

ls -ld */
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Vijay Gawas
  • 79
  • 1
  • 2
6

Here is what I am using

ls -d1 /Directory/Path/*;

Banning
  • 148
  • 2
  • 11
4

Test whether the item is a directory with test -d:

for i in $(ls); do test -d $i && echo $i ; done
muru
  • 4,232
  • 30
  • 69
nmarques
  • 131
  • 5
3

*/ is a filename matching pattern that matches directories in the current directory.

To list directories only, I like this function:

# Long list only directories
llod () {
  ls -l --color=always "$@" | grep --color=never '^d'
}

Put it in your .bashrc file.

Usage examples:

llod       # Long listing of all directories in current directory
llod -tr   # Same but in chronological order oldest first
llod -d a* # Limit to directories beginning with letter 'a'
llod -d .* # Limit to hidden directories

Note: it will break if you use the -i option. Here is a fix for that:

# Long list only directories
llod () {
  ls -l --color=always "$@" | egrep --color=never '^d|^[[:digit:]]+ d'
}
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Robin A. Meade
  • 1,003
  • 10
  • 12
3

FYI, if you want to print all the files in multi-line, you can do a ls -1 which will print each file in a separate line. file1 file2 file3

warfreak92
  • 284
  • 2
  • 15
2

I partially solved it with:

cd "/path/to/pricipal/folder"

for i in $(ls -d .*/); do sudo ln -s "$PWD"/${i%%/} /home/inukaze/${i%%/}; done

 

    ln: «/home/inukaze/./.»: can't overwrite a directory
    ln: «/home/inukaze/../..»: can't overwrite a directory
    ln: accesing to «/home/inukaze/.config»: too much symbolics links levels
    ln: accesing to «/home/inukaze/.disruptive»: too much symbolics links levels
    ln: accesing to «/home/inukaze/innovations»: too much symbolics links levels
    ln: accesing to «/home/inukaze/sarl»: too much symbolics links levels
    ln: accesing to «/home/inukaze/.e_old»: too much symbolics links levels
    ln: accesing to «/home/inukaze/.gnome2_private»: too much symbolics links levels
    ln: accesing to «/home/inukaze/.gvfs»: too much symbolics links levels
    ln: accesing to «/home/inukaze/.kde»: too much symbolics links levels
    ln: accesing to «/home/inukaze/.local»: too much symbolics links levels
    ln: accesing to «/home/inukaze/.xVideoServiceThief»: too much symbolics links levels

Well, this reduce to me, the major part :)

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
inukaze
  • 283
  • 1
  • 5
  • 13
  • What is meant by *"this reduce to me"* (seems incomprehensible)? – Peter Mortensen Jan 24 '21 at 15:02
  • on spanish is perfectly clear. but on english probably don't sounds with sense. that just mean a lot of the part is just less, just think in it the reduction of 80% of thing i don't need to do for make it work. – inukaze Feb 14 '21 at 12:42
2

One-liner to list directories only from "here".

With file count.

for i in `ls -d */`; do g=`find ./$i -type f -print| wc -l`; echo "Directory $i contains $g files."; done
A-312
  • 10,203
  • 4
  • 38
  • 66
BHG
  • 21
  • 1
2

Adding on to make it full circle, to retrieve the path of every folder, use a combination of Albert's answer as well as Gordans. That should be pretty useful.

for i in $(ls -d /pathto/parent/folder/*/); do echo ${i%%/}; done

Output:

/pathto/parent/folder/childfolder1/
/pathto/parent/folder/childfolder2/
/pathto/parent/folder/childfolder3/
/pathto/parent/folder/childfolder4/
/pathto/parent/folder/childfolder5/
/pathto/parent/folder/childfolder6/
/pathto/parent/folder/childfolder7/
/pathto/parent/folder/childfolder8/
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
BilliAm
  • 550
  • 1
  • 6
  • 23
2

Using Perl:

ls | perl -nle 'print if -d;'
glennsl
  • 23,127
  • 11
  • 49
  • 65
2

Here is a variation using tree which outputs directory names only on separate lines, yes it's ugly, but hey, it works.

tree -d | grep -E '^[├|└]' | cut -d ' ' -f2

or with awk

tree -d | grep -E '^[├|└]' | awk '{print $2}'

This is probably better however and will retain the / after directory name.

ls -l | grep "^d" | awk '{print $9}'
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
lacostenycoder
  • 8,780
  • 3
  • 23
  • 38
1
file * | grep directory

Output (on my machine) --

[root@rhel6 ~]# file * | grep directory
mongo-example-master:    directory
nostarch:                directory
scriptzz:                directory
splunk:                  directory
testdir:                 directory

The above output can be refined more by using cut:

file * | grep directory | cut -d':' -f1
mongo-example-master
nostarch
scriptzz
splunk
testdir

* could be replaced with any path that's permitted
 file - determine file type
 grep - searches for string named directory
 -d - to specify a field delimiter
 -f1 - denotes field 1
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
AVS
  • 121
  • 2
1

Here is what I use for listing only directory names:

ls -1d /some/folder/*/ | awk -F "/" "{print \$(NF-1)}"
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Robert Lujo
  • 12,924
  • 4
  • 46
  • 64
1

To answer the original question, */ has nothing to do with ls per se; it is done by the shell/Bash, in a process known as globbing.

This is why echo */ and ls -d */ output the same elements. (The -d flag makes ls output the directory names and not contents of the directories.)

flow2k
  • 2,619
  • 24
  • 40