Typing ls -l in the Shell explained.

David Peralta
6 min readAug 29, 2019

In this blog, we are going to see what happens ‘under the hood’ when we type the command ‘ls -l’ in the shell. In a general way, the ‘ls -l’ command is used to list files of the current working directory in a long format.

ls -l example

It seems very simple explained this way, but there are lots of things happening there. First, lest talk about the shell, it is a user interface used in most operating systems based on Unix and similars, like GNU/Linux. Basically, it is a program which we use to communicate with the kernel, it is also known as ‘commands line interpreter’.

The Kernel is a fundamental software for the operating system, it is responsible for giving different programs access to the hardware, like inputs, outputs, memory, and others, in a safe an stable way. On most system, the kernel is the first program loaded when we turn on our machines.

When we open the shell, the first we see is a prompt and the shell waiting for us to type some command, the prompt we see the first time it a set of characters that give us some information about the current user session, in Linux, this prompt appearance and info is defined in the PS1 environment variable. When the shell is waiting for commands we see it.

Here are two examples.

Now, let's go deeper, the general process when of the shell when we type ‘ls -l’ will be:

> Read Input

> Check for alias

> Check builtins

> Look for the executable in the PATH if necessary.

> Execute


The shell will read all characters we type in the STANDARD INPUT, basically, in this part of the process, we can type and modify characters until we hit the return key or cancel with signals calls as EOF (Ctrl + D for example, see more about it here.).


Reading the line from the input is basically making a string with all characters we type before, then the Shell creates a data structure of words, it means that divide the string into words or tokens, usually by using space ‘ ’, tabs ‘\t’, and return ‘\r’ as delimiters. For example:

If we type: 
"/bin/ls -l /usr/"
The resulting data structure (array of char pointers) will be:
char *arguments[] = {"/bin/ls", "-l", "/usr/", NULL};


Most shells have alias feature, it is a tool that allows us to create aliases for different commands, it means, while ‘ls -l’, print the list of files and directories in the current path, we can define aliases that perform more complex commands with a simple name.

For example, we can define an alias for ‘ls -l’ as ‘ll’, so if we type ‘ll’ it will perform the corresponding action ‘ls -l’. The Shell first checks for aliases by looking for it in specific hidden text files in the computer, usually, we find these files in the home directory, but it can be in different parts.

If the Shell finds the alias automatically replace it for the next processes. If there is no alias, the command keeps the same.

4- Check for Builtins

The builtins are special programs that live inside the shell, it means that are part of the shell source code, for this reasons these programs run faster because don’t have to look for executables and do the PATH processes(explained in the next part). For example, the command ‘cd’ is a builtin function, it is the command to change working directories inside the shell. But, ls is not a builtin, so the next step is to check if the function is in the PATH.


The PATH is an environmental variable, those variables show us different information about the context of the shell. See the environmental variables typing the command ‘printenv’, and if you want to see the PATH variable do it as the example.

type echo $PATH

Basically this variable has the directories where are the executable files, each directory is separated by a ‘:’ character, ‘ls’ executable is usually in the /bin directory.

When we type ls, if there is no alias set for this command, the shell will get to this point. Basically, now it will verify where is the ‘ls’ command by checking if it exists or not in those directories. If the $PATH for any reason doesn’t exist or has been deleted, it will print an error.

if the path doesn’t exist example

But of course, it is not the usual case, so as /bin has the ‘ls’ executable, it will run it.


Sounds simple, but the shell actually create a copy of itself and replace it for the ‘ls’ execute the process, this is called fork.

The fork() essentially duplicate the shell process, this new process is called child process, as we know execution process has certain address in the machine, so the original shell will be linked to the new process, and all of it will be changed for the ‘ls’. Sounds complicated? Look it this way.

fork() ls from bash

The ‘bash’ is a popular Shell program, in this example, we can see that bash create the fork(), as the ‘ls’ can be executed, means it exists and has a PATH, the execvp() function replaces the current process image with a new process image. And the wait() functions wait for the child process to change the state or finish, fork(), wait(), execvp() are systems calls.

The execvp(), function manages the ‘ls’ option ‘-l’, as we saw before the tokenization returns a pointer to an array of strings, which are passed to the execvp(), this is the syntax:

int execv(const char *path, char *const argv[], char *const envp[]);

For a better understanding of this, let’s look at the concepts system call and applications. A SYSTEM CALL is a call made from an application or program to the operating system. It means the shell will communicate with the kernel for the execution, it gives privileges to the executed file.

We are not going to go deeper in this matter, it only allows us to ‘see’ the logical step by step of the execution process of ‘ls’.

When the ‘ls’ finish, the prompt is printed on the standard output again and shell waits for the user to type another command.

Sounds like a lot, well this is a very simple blog for a subject like this, the best way to understand deeper processes of programming, what about making your own shell as a challenge?