What happens when you type ls -l on your shell?

Checha Giudice
5 min readNov 25, 2020

--

Let’s start with the Earth’s origin (or the basics):

What’s a shell?

“The shell is a program that takes commands from the keyboard and gives them to the operating system to perform. In the old days, it was the only user interface available on a Unix-like system such as Linux”. (Definition taken from https://linuxcommand.org/lc3_lts0010.php ).

So, basically a shell is like a marker (that blinking little stick you see on your screen beside the $ sign) with which you can write orders (or commands), and the blackboard (operating system) will execute them (or not, depending on whether you wrote your commands nicely).

You may be asking: what the shell has to do with anything?

Well, “ls -l” is one command you can try on your shell.

If this is your first time programming or looking at the command line or prompt on your virtual machine… don’t be afraid. It is not that hard to understand what this command “ls -l” actually does.

When you type it on your prompt, the shell will get the order and interpret it.

Like this:

Pic. 0

See? Not that hard.

So, what does ls -l do in this case? It finds every file (not hidden) in my current folder (named myfolder), and lists it with the pertinent information about each file: permissions, owner, creation date, etc..

That’s WHAT it does!

And you lived happily ever after… until I give you the HOWS of how the command does the work.

HOW do you do it then, ls?

Let’s start all over again.

Divide the command into the relevant factors and define them (in proper programming language, of course).

Now you have:

  • ls =>Command that lists the files in the working directory.
  • -l => Option given to ls to list the files in the working directory in long format.
  • (Enter key) => Press the Enter key so that the shell “executes” the command.

The man page of ‘ls’ on the shell shows us this info (type man ls on your shell and press Enter):

Pic. 1

But it does not explain the actual process.

When you type “ls -l”, and press Enter, the shell receives and recognizes the command.

HOW does the shell recognize the command (or any command, since we are on the subject)?

It interprets it, dah.

I’ll elaborate:

There are four types of commands:

Pic. 2
  • Executables: All files contained in the folder /usr/bin (that’s where ls is located).
  • Shell built-ins: Commands internally (like cd).
  • Shell functions: Shell scripts incorporated into the environment.
  • Alias: User built-in commands.

Now we know, ls is an executable file. And -l (being an option of ls) will alter ls behaviour when executed.

The process made after you hit Enter goes like this:

The shell will recognize your input as a command (still does not know which command yet), and with a function called getline (stdio.h library: ssize_t getline(&buffer, &size, stdin)) that will take the command and store it in an array (buffer) with a previously declared size, and will be passed to another function (sparse function), so the tokenization will start.

This sparse function, called strtok (string.h library: char *strtok(char *str, const char *delim)), will separate the array (command) into separate arrays because the function recognizes that the space between the words separates different arrays.

Pic. 3

Once the shell gets the separate arrays, it checks whether the first argument (arg[0] in Pic. 3) is a built-in function (which we know is not true because I already told you several paragraphs back). Once it fails, will then check if arg[0] is a built-in script (also false).

Finally, the shell will check the PATH. The PATH is an environment variable (one of the most important ones) that indicates where the executable files must be looked for. I already told you where to look, but your machine doesn’t know that.

If you want to know what your environment variables are, just type “env” on your prompt and hit Enter. It will look something like this.

Pic. 4

The line highlighted in red starts with “PATH=”, and you will notice directories names separated from each other with a “:”. This “:” allows the shell to look inside each directory to look for the command (remember arg[0]).

After the shell finds arg[0], it will proceed to check for permissions. It will not happen in this case but if the executable file did not have any executable permissions, the shell will print on your screen something like “Permission denied”. If the file was not found (again, not this case), it will print “File or directory not found”.

Finally (more or less), once the shell finds the path or location of your executable file (arg[0] == “ls”), it will execute the file according to the flags (options) you have given to it (arg[1] == “-l”).

Now WHY did I say “more or less”, because it sounds simple and straightforward, but no. The thing is, once the shell finds the executable file, the shell itself will create a fork (sys/types.h and unistd.h libraries: pid_t fork(void)), which is a child process, a copy of the parent process, making both programs run simultaneously.

First goes the child process who executes the command from the command line that was inputted, and the parent process will wait for the child process to sign to it that it has finished running. If this process ends successfully, then the shell will print an output and return to step one (before you wrote “ls -l”).

And that will end the process. So then this happens:

Back to Pic. 0

Here’s an image of a more simplification way to understand what THE PROCESS behind typing ls -l is:

Pic 5 — the WHOLE process

Now you get it! Not that hard, right?

--

--