A trip with ls -l

What happens when you type ls -l in the shell ?

Nour Ouhichi
5 min readApr 14, 2020

In order to understand well what happens then , you should first have a clear idea of what the shell is. So let me introduce the concept ;

The shell:

In easy terms, the shell is what is between you as a user and the operating system. It takes the commands from the standard input (which is the keyboard ) and sends it to be performed.

What is ls result on the standard output (the screen) ?

ls, alone, simply lists a directory content (files, other directories, PNG, mp4..) with options(also called arguments) it could make several other things. Let us take the example of -l.

As the manual says, -l shows extra information about the content as permissions, last time it was modified …

these information are provided by the operating system , but how can a simple sentence (or a string as a programmer would say), be interpreted by the operating system as a command to provide the standard output with the required information?

  • This process is long but never hard to understand .First the system has to prepare itself to receive an input; this step includes displaying the message and waiting for the user to type the command . What ever the user is going to type will go automatically to the standard input file; in fact the process will be the following .
char *buffer = NULL;
size_t biffer_size = 0;
getline(&buffer, &buffer_size, stdin);

about getline function: This function allocates memory by itself for the user input as long as the user did not specify the size of the buffer. Which reduces memory loss and requires freeing manually the allocated memory when you no longer need the buffer.

  • Now that the shell has the command line, it has to parse it in order to get the command and its arguments apart, this happens by splitting the line into tokens which we know that every word in it is separated from the other by a space which in its turn will be excluded as it is the delimiter in a system call the shell uses for this process; strtok .This process is called tokenization. The shell now has a tow dimensional array (array of strings) containing the command and its argument.(For more information about the strtok sys call please read the man by typing man strtok in the terminal )
  • In a third place, the trip of the shell in finding a signification of ls in the operating system begins..

1. The shell checks if the command is a built-in command. This step is important and should be done in the first place in this trip ; because if the command is a built-in (as cd, exit, env ..) no other program would be executed for the command to be performed unlike our example (ls command which is not a built-in command ). Noticing that the command is not a built-in moves us to the next step where more system calls are interfered.

2. The system now needs to find the executable or program for the command ; the search is done by interfering a environmental variable know as $PATH; this variable is a simple string containing paths to all executable programs in your system separated by “:”, entering $PATH in the terminal will display you these paths. Now the shell, to perform this task needs to call functions as find_user_command_in_path(), searching here is not hard but just searching has no point, each search must be accompanied by a function that the shell invokes to assume if the command exists as an executable or not; it is time for the stat() function which has the following synopsis:

int stat(const char *pathname, struct stat *statbuf);

this function simply gets you information about a file , in fact each file will provide the function with the following structure :

struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* Inode number */
mode_t st_mode; /* File type and mode */
nlink_t st_nlink;/* Number of hard links */
uid_t st_uid; /* User ID of owner */
gid_t st_gid; /* Group ID of owner */
dev_t st_rdev; /* Device ID (if special file) */
off_t st_size; /* Total size, in bytes */
blksize_t st_blksize;/* Block size for filesystem I/O */
blkcnt_t st_blocks; /* Number of 512B blocks allocated */
}

As zero is returned in success , as in our case the shell would be interacting with other useful system calls which may be of a new concept for you.

At this point the shell starts performing a process that is similar to the fork() function or fork itself (depending on the system); simply put, this function creates a duplicate copy of the calling process as its child, in fact fork copies stack section, heap section, data section, environment variable, command line arguments from parent. It actually allows the shell to perform tasks independently one another. In our case the shell uses the process to execute the program ls by another system call inside the child which is execve().

As running a child process does not stop running the parent process. the parent must wait, otherwise we will not have the wanted result . In this context the function wait() may be of a big use ; if the child process succeeded in being created waiting is required .

The execution of the command happens by entering the array made by tokenization as a parameter in the execve function, in fact its synopsis is the following:

int execve(const char *filename, char *const argv[],char *const envp[]);

where argv is an array of argument strings passed to the new program. By
convention, the first of these strings (i.e., argv[0]) should contain
the filename associated with the file being executed. envp is an array
of strings, conventionally of the form key=value, which are passed as
environment to the new program. The argv and envp arrays must each
include a null pointer at the end of the array.And this is exactly what we have after tokenizing the command line at the first place.

--

--

Nour Ouhichi

Software engineering student at Holberton school with the passion of sharing my knowledge. Simplified language articles about different programming languages.