"ls" (at least the implementations that I know of) use fts_open, fts_read ... to traverse a file hierarchy. These are "non-recursive" methods which maintain a list of the visited directories internally.

Use "man fts_read" or http://linux.die.net/man/3/fts_read to get more information about these functions.

Answer from Martin R on Stack Overflow
🌐
RapidTables
rapidtables.com β€Ί code β€Ί linux β€Ί ls β€Ί ls-r.html
ls -r -R command in Linux/Unix
$ ls -R .: Desktop Downloads Pictures Templates Videos Documents Music Public todo.txt ./Desktop: examples.desktop ubiquity-gtkui.desktop ./Documents: ./Downloads: ./Music: ./Pictures: ls-a.png ls-full-path.png ls-l.png ls-s.png ./Public: ./Templates: ./Videos: $
🌐
Reddit
reddit.com β€Ί r/programminghorror β€Ί using the ls -r command effectively - how to set the output to list all the data simply
r/programminghorror on Reddit: using the LS -R command effectively - how to set the output to list all the data simply
November 3, 2023 -

using the LS -R command effectively - how to set the output to list all the data simply

see what i have now:

`./Luca_V: 00_data_1.pdf 1_data_2.pdf 1_data_3.pdf data_4.pdf data_5.pdf data_6.pdf data_7_.pdf data_8_.pdf data_9.pdf __data_10.pdf

./David_V: 00_data_1.pdf 1_data_2.pdf 1_data_3.pdf data_4.pdf data_5.pdf data_6.pdf data_7.pdf data_8_.pdf data_9.pdf __data_10.pdf

`

i want to have the data put so that all the files are listed in a simple list view:

🌐
University of Cambridge
maths.cam.ac.uk β€Ί computing β€Ί linux β€Ί unixinfo β€Ί ls
The ls command | Computing
The next three columns are the ... or deleted). The last column is the name of the file. ls -R gives a recursive listing, including the contents of all subdirectories and their subdirectories and so on....
🌐
EITCA Academy
eitca.org β€Ί home β€Ί what is the purpose of the "ls" command in linux?
What is the purpose of the "ls" command in Linux? - EITCA Academy
August 5, 2023 - The "ls" command in Linux is a fundamental and versatile tool used for listing files and directories in a specified location within the file system. It plays a important role in Linux system administration and is widely utilized in various cybersecurity tasks.
🌐
High Performance Computing
hpc.ncsu.edu β€Ί Documents β€Ί unixtut β€Ί unix1.html
Linux Tutorial One
Characters written in bold typewriter font are commands to be typed into the computer as they stand. Characters written in italic typewriter font indicate non-specific file or directory names. Words inserted within square brackets [Ctrl] indicate keys to be pressed. ... means "at the Linux prompt $, type ls followed by the name of some directory, then press the key marked Enter"
Top answer
1 of 5
66

First off, let's define an arbitrary folder structure:

.
β”œβ”€β”€ a1 [D]
β”‚   β”œβ”€β”€ b1 [D]
β”‚   β”‚   β”œβ”€β”€ c1
β”‚   β”‚   β”œβ”€β”€ c2 [D]
β”‚   β”‚   β”‚   β”œβ”€β”€ d1
β”‚   β”‚   β”‚   β”œβ”€β”€ d2
β”‚   β”‚   β”‚   └── d3
β”‚   β”‚   β”œβ”€β”€ c3
β”‚   β”‚   β”œβ”€β”€ c4
β”‚   β”‚   └── c5
β”‚   └── b2 [D]
β”‚       β”œβ”€β”€ c6
β”‚       └── c7
β”œβ”€β”€ a2 [D]
β”‚   β”œβ”€β”€ b3 [D]
β”‚   β”‚   β”œβ”€β”€ c8
β”‚   β”‚   └── c9
β”‚   └── b4 [D]
β”‚       β”œβ”€β”€ c10 
β”‚       └── c11
β”œβ”€β”€ a3 [D]
β”‚   β”œβ”€β”€ b5
β”‚   β”œβ”€β”€ b6
β”‚   └── b7
└── a4 [D]

When we do ls, we get the output of the base folder only:

a1 a2 a3 a4

However, when we call ls -R, we get something different:

.:
a1  a2  a3  a4

./a1:
b1  b2

./a1/b1:
c1  c2  c3  c4  c5

./a1/b1/c2:
d1  d2  d3

./a1/b2:
c6  c7

./a2:
b3  b4

./a2/b3:
c8  c9

./a2/b4:
c10  c11

./a3:
b5  b6  b7

./a4:

As you can see, it's running ls on the base folder, and then all the child folders. And all the grandchild folders, ad infinitum. Effectively, the command is going through each folder recursively until it hits the end of the directory tree. At that point, it comes back up a branch in the tree and does the same thing for any sub-folders, if any.

Or, in pseudo-code:

recursivelyList(directory) {
    files[] = listDirectory(directory)              // Get all files in the directory
    print(directory.name + ":\n" + files.join(" ")) // Print the "ls" output
    for (file : files) {                            // Go through all the files in the directory
        if (file.isDirectory()) {                   // Check if the current file being looked at is a directory
            recursivelyList(directory)              // If so, recursively list that directory
        }
    }
}

And because I can, a reference Java implementation of the same.

2 of 5
23

There are, in effect, two closely coupled questions you may be asking.

  • Why is the process of walking to each entry in a filesystem hierarchy an inherently recursive process? This is addressed by the other answers, such as Zanna's and Kaz Wolfe's.
  • How is the technique of recursion used in the implementation of ls? From your phrasing ("How is recursion used in the process?"), I think this is part of what you want to know. This answer addresses that question.

Why it makes sense for ls to be implemented with a recursive technique:

FOLDOC defines recursion as:

When a function (or procedure) calls itself. Such a function is called "recursive". If the call is via one or more other functions then this group of functions are called "mutually recursive".

The natural way to implement ls is to write a function that constructs a list of filesystem entries to be displayed, and other code to process path and option arguments and to display the entries as desired. That function is highly likely to be implemented recursively.

During option processing, ls will determine if it has been asked to operate recursively (by being invoked with the -R flag). If so, the function that builds a list of entries to be displayed will call itself once for each directory it lists, except . and ... There may be separate recursive and nonrecursive versions of this function, or the function may check each time if it is supposed to be operating recursively.

Ubuntu's /bin/ls, the executable that runs when you run ls, is provided by GNU Coreutils, and it has many features. As a result, its code is somewhat longer and more complicated than you might expect. But Ubuntu also contains a simpler version of ls, provided by BusyBox. You can run this by typing busybox ls.

How busybox ls uses recursion:

ls in BusyBox is implemented in coreutils/ls.c. It contains a scan_and_display_dirs_recur() function that is invoked to print a directory tree recursively:

static void scan_and_display_dirs_recur(struct dnode **dn, int first)
{
    unsigned nfiles;
    struct dnode **subdnp;

    for (; *dn; dn++) {
        if (G.all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
            if (!first)
                bb_putchar('\n');
            first = 0;
            printf("%s:\n", (*dn)->fullname);
        }
        subdnp = scan_one_dir((*dn)->fullname, &nfiles);
#if ENABLE_DESKTOP
        if ((G.all_fmt & STYLE_MASK) == STYLE_LONG || (G.all_fmt & LIST_BLOCKS))
            printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
#endif
        if (nfiles > 0) {
            /* list all files at this level */
            sort_and_display_files(subdnp, nfiles);

            if (ENABLE_FEATURE_LS_RECURSIVE
             && (G.all_fmt & DISP_RECURSIVE)
            ) {
                struct dnode **dnd;
                unsigned dndirs;
                /* recursive - list the sub-dirs */
                dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
                dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
                if (dndirs > 0) {
                    dnsort(dnd, dndirs);
                    scan_and_display_dirs_recur(dnd, 0);
                    /* free the array of dnode pointers to the dirs */
                    free(dnd);
                }
            }
            /* free the dnodes and the fullname mem */
            dfree(subdnp);
        }
    }
}

The line where the recursive function call takes place is:

                    scan_and_display_dirs_recur(dnd, 0);

Seeing the recursive function calls as they happen:

You can see this in operation if you run busybox ls in a debugger. First install the debug symbols by enabling -dbgsym.ddeb packages and then installing the busybox-static-dbgsym package. Install gdb as well (that's the debugger).

sudo apt-get update
sudo apt-get install gdb busybox-static-dbgsym

I suggest debugging coreutils ls on a simple directory tree.

If you don't have one handy, make one (this works the same way as the mkdir -p command in WinEunuuchs2Unix's answer):

mkdir -pv foo/{bar/foobar,baz/quux}

And populate it with some files:

(shopt -s globstar; for d in foo/**; do touch "$d/file$((i++))"; done)

You can verify busybox ls -R foo works as expected, producing this output:

foo:
bar    baz    file0

foo/bar:
file1   foobar

foo/bar/foobar:
file2

foo/baz:
file3  quux

foo/baz/quux:
file4

Open busybox in the debugger:

gdb busybox

GDB will print some information about itself. Then it should say something like:

Reading symbols from busybox...Reading symbols from /usr/lib/debug/.build-id/5c/e436575b628a8f54c2a346cc6e758d494c33fe.debug...done.
done.
(gdb)

(gdb) is your prompt in the debugger. The first thing you'll tell GDB to do on this prompt is to set a breakpoint at the start of the scan_and_display_dirs_recur() function:

b scan_and_display_dirs_recur

When you run that, GDB should tell you something like:

Breakpoint 1 at 0x5545b4: file coreutils/ls.c, line 1026.

Now tell GDB to run busybox with ls -R foo (or whatever directory name you want) as its arguments:

run ls -R foo

You may see something like this:

Starting program: /bin/busybox ls -R foo

Breakpoint 1, scan_and_display_dirs_recur (dn=dn@entry=0x7e6c60, first=1) at coreutils/ls.c:1026
1026    coreutils/ls.c: No such file or directory.

If you do see No such file or directory, as above, that's okay. The purpose of this demonstration is just to see when the scan_and_display_dirs_recur() function has been called, so GDB doesn't need to examine the actual source code.

Notice that the debugger hit the breakpoint even before any directory entries were printed. It breaks on the entrace to that function, but the code in that function must run for any directories to be enumerated for printing.

To tell GDB to continue, run:

c

Each time scan_and_display_dirs_recur() is called, the breakpoint will be hit again, so you will get to see recursion in action. It looks like this (including the (gdb) prompt and your commands):

(gdb) c
Continuing.
foo:
bar    baz    file0

Breakpoint 1, scan_and_display_dirs_recur (dn=dn@entry=0x7e6cb0, first=first@entry=0) at coreutils/ls.c:1026
1026    in coreutils/ls.c
(gdb) c
Continuing.

foo/bar:
file1   foobar

Breakpoint 1, scan_and_display_dirs_recur (dn=dn@entry=0x7e6cf0, first=first@entry=0) at coreutils/ls.c:1026
1026    in coreutils/ls.c
(gdb) c
Continuing.

foo/bar/foobar:
file2

foo/baz:
file3  quux

Breakpoint 1, scan_and_display_dirs_recur (dn=dn@entry=0x7e6cf0, first=first@entry=0) at coreutils/ls.c:1026
1026    in coreutils/ls.c
(gdb) c
Continuing.

foo/baz/quux:
file4
[Inferior 1 (process 2321) exited normally]

The function has recur in its name... does BusyBox only use it when the -R flag is given? In the debugger, this is easy to find out:

(gdb) run ls foo
Starting program: /bin/busybox ls foo

Breakpoint 1, scan_and_display_dirs_recur (dn=dn@entry=0x7e6c60, first=1) at coreutils/ls.c:1026
1026    in coreutils/ls.c
(gdb) c
Continuing.
bar    baz    file0
[Inferior 1 (process 2327) exited normally]

Even without -R, this particular implementation of ls uses the same function to find out what filesystem entries exist and show them.

When you want to quit the debugger, just tell it:

q

How scan_and_display_dirs_recur() knows if it should call itself:

Specifically how does it work differently when the -R flag is passed? Examining the source code (that may not be the exact version on your Ubuntu system) reveals that it checks its internal data structure G.all_fmt, where it stores what options it has been invoked with:

            if (ENABLE_FEATURE_LS_RECURSIVE
             && (G.all_fmt & DISP_RECURSIVE)

(If BusyBox has been compiled without support for -R, then it will also not attempt to display filesystem entries recursively; that's what the ENABLE_FEATURE_LS_RECURSIVE part is about.)

Only when G.all_fmt & DISP_RECURSIVE is true does the code that contains the recursive function call get run.

                struct dnode **dnd;
                unsigned dndirs;
                /* recursive - list the sub-dirs */
                dnd = splitdnarray(subdnp, SPLIT_SUBDIR);
                dndirs = count_dirs(subdnp, SPLIT_SUBDIR);
                if (dndirs > 0) {
                    dnsort(dnd, dndirs);
                    scan_and_display_dirs_recur(dnd, 0);
                    /* free the array of dnode pointers to the dirs */
                    free(dnd);
                }

Otherwise, the function just runs once (per directory specified on the command line).

Find elsewhere
🌐
LinuxQuestions.org
linuxquestions.org β€Ί questions β€Ί linux-newbie-8 β€Ί ls-*-vs-ls-r-commands-4175674976
ls * vs. ls -R commands
I'm having a little trouble figuring out how ls * differs from the command ls -R (which the man page says "lists subdirectories recursively."
🌐
DigitalOcean
digitalocean.com β€Ί community β€Ί tutorials β€Ί ls-command-in-linux-unix
ls command in Linux/UNIX | DigitalOcean
August 4, 2022 - Aliases are customized or modified commands in Linux shell which are used in the place of the original commands. We can create an alias for the ls command this way Syntax ... What this does is that it tells the system to execute the ls -l command instead of the ls command. Be sure to observe that the output you get when running ...
🌐
Linux.org
linux.org β€Ί docs β€Ί man1 β€Ί ls.html
ls - list directory contents at Linux.org
--author with -l, print the author of each file -b, --escape print C-style escapes for nongraphic characters --block-size=SIZE scale sizes by SIZE before printing them; e.g., '--block-size=M' prints sizes in units of 1,048,576 bytes; see SIZE format below -B, --ignore-backups do not list implied entries ending with ~ -c with -lt: sort by, and show, ctime (time of last modification of file status information); with -l: show ctime and sort by name; otherwise: sort by ctime, newest first -C list entries by columns --color[=WHEN] colorize the output; WHEN can be 'never', 'auto', or 'always' (the d
🌐
GeeksforGeeks
geeksforgeeks.org β€Ί ls-command-in-linux
ls Command in Linux - GeeksforGeeks
April 11, 2025 - The ls -l command in Linux is used to list the detailed information about the files and directories in the current folder. ... -rw-rw-r-- 1 maverick maverick 1176 Feb 16 00:19 1.c 1st Character – File Type: First character specifies the type ...
🌐
The Privacy Dad
theprivacydad.com β€Ί three-basic-linux-commands-ls-pwd-cd
Basic Linux Commands: ls, pwd, cd | Welcome to The Privacy Dad's Blog!
It helps to imagine your computer is a building with lots of floors, and that you can only be in one place at a time. The following commands can help you move up and down floors, open rooms and see what stuff is inside each room in your system. ... for example, if you are in your Documents directory, typing ls will list all the documents you have saved there
🌐
LinuxTeck
linuxteck.com β€Ί basic-ls-command-in-linux-with-examples
The Most 17 Useful 'ls' Command In Linux With Examples | LinuxTeck
December 28, 2025 - In the above example you can see a folder named 'linuxteck' and a file named 'test' are listed in the first place. These two files are the newly created ones. ... Note: Using the 'ls -lh' option will show the size of each file in a human-readable format. Reading files in bytes may get confusing instead we can read files in KB,MB,GB etc, it is much easier "e.g.," To read 5782242 bytes will get confused whereas to read 5.7 M is much more user-friendly. In addition, the ls command won't display the total amount of space taken up by the contents of the directory.
🌐
NIELIT
nielit.gov.in β€Ί gorakhpur β€Ί sites β€Ί default β€Ί files β€Ί Gorakhpur β€Ί ALevel_unix_08_May_2020_AKM.pdf pdf
The ls command is one of the basic commands that any Linux user should
The ls command is one of the basic commands that any Linux user should Β· know. It is used to list information about files and directories within the file
🌐
PhoenixNAP
phoenixnap.com β€Ί home β€Ί kb β€Ί sysadmin β€Ί linux ls command - how to list files and directories
Linux ls Command - How to List Files and Directories
June 30, 2025 - Print a comprehensive, long-format listing of all files and directories in the current directory, including hidden ones, along with detailed file information. To accomplish this, run: ... To print specific file types using the ls command, use ...
🌐
Wikipedia
en.wikipedia.org β€Ί wiki β€Ί Ls
ls - Wikipedia
1 month ago - -d Show information about a directory or symbolic link, rather than the contents of a directory or the link's target Β· -F Append a "/" to directory names, a "*" to executable files and a "@" to symbolic links ... $ ls -l drwxr--r-- 1 fjones editors 4096 Mar 2 12:52 drafts -rw-r--r-- 3 fjones editors 30405 Mar 2 12:52 edition-32 -r-xr-xr-x 1 fjones bookkeepers 8460 Jan 16 2022 edit.sh
🌐
Linux Foundation
refspecs.linuxfoundation.org β€Ί LSB_1.0.0 β€Ί gLSB β€Ί ls.html
ls
Aside from the parent directory, the -d option for ls lists only those items specified on the command line, listing only the names of directories, and not their contents, but listing nothing else in this file system path that is not specified by the command line file list. ... as well as the slash declared by SUS, this option will also display the *, @, and | characters for executables, links, and FIFOs, respectively.
🌐
Linuxize
linuxize.com β€Ί tags β€Ί ls
Ls | Linuxize
July 15, 2020 - ls command lists files and directories within the filesystem, displaying detailed information about them. Read more Β· Jul 15, 2020 Β· In this article, we will show you several different ways to find the number of files in a directory in Linux. Press ESC to close Β·