47

Is there a way to get ls to only display directories instead of files and directories?

From the man page:

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

So if I type it at the / directory I expect to see only directories. Instead it shows "."

$ cd /
$ ls -d
.

I was expecting ls -d to show me this:

$ ls -d
bin    data  home        opt    sbin  sys      var
boot   dev   lib         media  proc  selinux  tmp
cdrom  etc   lost+found  mnt    root  srv      usr

Is there a way to get ls to only display directories instead of files and directories?

Peter Mortensen
  • 12,090
  • 23
  • 70
  • 90
nelaaro
  • 13,149
  • 30
  • 84
  • 111
  • 18
    lsd *can* be very confusing. – boehj Oct 10 '11 at 09:12
  • 2
    @jin ls -d */ works. But why do I have to "*/" to get the out put I want. – nelaaro Oct 10 '11 at 09:33
  • 8
    @nelaar `-d` doesn't mean to list directories only, it means to not list directory contents. Try typing `ls */` and you'll see the contents of all the directories. – Jin Oct 10 '11 at 09:45
  • 2
    I have `ldir` alised to `ls -d */` in my `.bashrc` to make this easier... – DQdlM Oct 10 '11 at 11:09
  • possible duplicate of [How to list folders using bash commands?](http://superuser.com/questions/335376/how-to-list-folders-using-bash-commands) – Gaff Oct 11 '11 at 02:19
  • 1
    ls can give you tons of information but not one single flag for just showing directories... – DimiDak Feb 15 '19 at 14:00

7 Answers7

51

Your expectations are based upon DOS Think/Windows Think and are wrong. On MS-DOS, Windows, and indeed a few other IBM/Microsoft operating systems, wildcard expansion is done by the command itself, and things like the /a option to the dir command act as attribute filters during wildcard expansion. dir expands wildcards like *, which the command interpreter passes to it as-is, and if /a is specified it applies the appropriate filters to what is returned. (On some operating systems, the attribute filters can be given to the system call for enumerating a directory and the operating system kernel, or its filesystem drivers, applies them itself.)

On Unices and on Linux, wildcard expansion is done by the shell, and is oblivious to permissions. When, in the root directory, you do

ls *

what the ls command itself receives from the shell is (something like)

ls bin home opt var boot dev tmp etc lost+found root usr

What the -d/--directory option does is turn off what normally happens next. What normally happens next is that ls looks at each of its arguments in turn, sees that they are directories, and decides to enumerate their contents. For arguments that name files, it just prints out the information for the file itself. With the -d option, directories are treated just like files. So ls prints out the information for each of the directories that is passed as its arguments, just as it would do if they were files.

So -d is not a "print only directories" option. In fact, not only is there no such option; there cannot be such an option. Wildcard expansion is done by the shell, and (in a POSIX sh at least) there's no way to tell the shell to check permission and file type bits when it expands * into a list of names. To obtain a list of the names of directories alone, it is necessary either to use the find command, as explained by ztank1013, or to use the trick that a pathname ending with a slash implies the directory entry ., as explained by Jin. (Jin's trick ends up with the ls command receiving the arguments

ls bin/ home/ opt/ var/ boot/ dev/ tmp/ etc/ lost+found/ root/ usr/

because the pattern */ is in fact matching pathnames with two components, the second being empty, and so isn't quite doing what was desired. In particular, it will treat symbolic links pointing to directories as if they were directories.)

The behaviour of ls -d without a * is a simple extension of the above. One simply has to know one more thing about ls: When it is given no arguments, it assumes a default argument of .. Now without the -d option, the aforementioned behaviour leads to the contents of the directory named by . being enumerated and the information for its contents displayed. With the -d option, the directory . is treated just as if it were a file, and its own information is displayed, rather than its contents enumerated.

JdeBP
  • 26,613
  • 1
  • 72
  • 103
  • thanks you. I realize I was not thinking about wildcard expansion correctly. You have clarified that very well for me. You have helped a lot. I can actually say I get it. – nelaaro Oct 10 '11 at 10:26
  • 3
    "there _cannot_ be such an option" - that's a rather strong statement - the implementation of such an option is obvious: see a non-directory argument and ignore it. What _can't_ be implemented is the equivalent of `dir /s *.txt` [without resorting to quoting wildcards as for find] – Random832 Oct 10 '11 at 13:25
  • 1
    It's a strong and true statement for a system like Unix where wildcard expansion is divorced from entry type checking. Your hypothetical implementation is erroneous. What you describe _still expands `*` to include the non-directories_, with all of the filesystem race conditions and subtle scripting connotations that that entails. Whereas `dir /a:d` will, on operating systems that have this functionality in the system API, skip non-directories immediately without even returning them to application-mode code. – JdeBP Oct 10 '11 at 15:09
  • 6
    Right, but there's no logical reason ls _can't_ have an option which will filter out items that were passed in as arguments. The fact that "wildcard expansion is divorced from entry type checking" is irrelevant, since this has nothing _at all_ to do with wildcard expansion - only entry type checking, which there is no reason it cannot be done _entirely_ within ls. If ls --color can shade them blue, ls -F can put a / after them, and ls -l can put a 'd' in the mode, then some other hypothetical option could omit them. That such an option does not exist doesn't mean it "_cannot_". – Random832 Oct 10 '11 at 17:15
  • Kiddo, it's not only relevant; it's the core of the problem. If you don't understand that, you don't understand one of the fundamentals of Unix. Think! Start by re-reading what I wrote and _think_ about what race conditions and subtle scripting connotations I referred to. The race conditions are fairly well known, and the scripting connotations can be seen with a little applied thought. – JdeBP Oct 29 '11 at 21:53
  • 5
    Random832 makes a decent point: `ls` *could* have an option to filter files or directories, and this isn't mutually exclusive with having the shell do expansion. There's no race condition within ls: it can filter it as it stats the files normally. (There is already a race between the shell expansion and ls, FWIW.) I think shell expansion is only part of the reason: shell expansion (and filtering in general) is not implemented in ls because it would need to be implemented again in cp, mv, etc. Unix is a "do one thing and do it well". If you need advanced filtering, there are tools for that. – Thanatos Mar 20 '13 at 05:45
  • 2
    The `ls -p` option adds a `/` symbol to directories only. There is no reason not to extend the output printing capabilities with something that does only output these (rather than `ls -p | grep /`). – Anne van Rossum Mar 14 '14 at 09:00
  • 1
    You and Thanatos, even though I've explained it twice already, have entirely missed the point. It's not about `ls` being altered to treat directories differently, as if somehow it didn't already. _We know that it can and it does. That is right there in the answer._ You've both not thought about what I said to think about, including the fundamentals of Unix, which mean that there's no way for an option to `ls` to control shell wildcard expansion. Random832 was specifically contradicting _that_ statement of mine, and providing as supposed proof a mechanism that doesn't actually work as claimed. – JdeBP Mar 14 '14 at 22:00
  • 3
    @JdeBP, ad-hominem attacks ("kiddo") and arrogance (everybody except you does not know "the fundamentals of Unix") do not belong to a technical discussion. Furthermore, you're pulling a straw man when you argue that a command option cannot control wildcard expansion done by shell. _Of course, it cannot!_ Neither Random832, nor Thanatos, nor Anne van Rossum, and not even the question itself mention wildcards in any way. The objection was that `ls --only-directories .` option could be added to `ls` eventually. – mkalkov Aug 07 '15 at 09:51
  • 2
    Answer is pedantic and presumptuous. Consider *ls -l* which lists all the stuff in the working directory. One might very reasonably expect *ls -d* to just list all the *directories* in the current directory (especially given the output of *man ls*, which OP quoted). The convention of showing everything in the working directory, subject to some constraint set by an option flag, is ubiquitous in bash. There is nothing weird or MSDOSish about it at all. Whatever happened to just answering the question. – neuronet Jun 16 '16 at 16:49
  • Ls could just add the "directories only" flag. With this hack "ls -d */" you have to be in the directory to see subdirectories. – DimiDak Feb 15 '19 at 14:06
  • Myy expectations have nothing to do with Windows. Have a downvote. I didn't read the rest. – felwithe Mar 30 '21 at 22:14
30

You can usels -d */, or ls -d .*/ for hidden directories.

Jin
  • 4,323
  • 27
  • 31
13

Try this

 find . -mindepth 1 -maxdepth 1 -type d

to get just directories under your current location.

tjanez
  • 173
  • 1
  • 5
ztank1013
  • 501
  • 2
  • 9
5

You might also like

tree -d 

which neatly lists all directories and subdirectories with a graphical depiction of the tree structure.

fuenfundachtzig
  • 757
  • 1
  • 7
  • 13
5

If you want to see directories only with detail like ls -l (ELL) option then you can use below:

find -maxdepth 1 -type d -ls;

Above will only give you the detail like you get with the -l option.

Adnan Bhatti
  • 151
  • 1
  • 3
  • 1
    It may be even better to use `find -maxdepth 1 -type d -exec ls -d {} +` to get output in the usual `ls`format. – mkalkov Aug 07 '15 at 09:40
4

If you want to get the job done in another way, try this:

ls -l | grep ^d

Although a single tool is enough in this situation. Pipe line is always there to help you. I like the flexibility in Linux, which I wish you do.

Bohr
  • 534
  • 5
  • 10
  • 1
    This is going to filter out more than directories. – ocodo Sep 01 '13 at 00:05
  • 2
    @Slomojo, this will filter out everything but directories. What did you mean? – mkalkov Aug 07 '15 at 09:37
  • Yeah, you need the -l (long) so that permissions head up the line. "d" for directory. Plus you get a "long" listing of a directory entry which is nice. – geoO Dec 29 '16 at 05:30
1

I hope this will rectify your need. The below command will list only directories in a given path.

ls -F <path> | grep /

Example:

ls -F ~ | grep /
Peter Mortensen
  • 12,090
  • 23
  • 70
  • 90
Thiyagu ATR
  • 111
  • 4