What happens when you type ls -l in the shell
At run-time, the shell program begins by executing the getline(...)
function. getline
pauses program execution, waiting for the user to type something. Let’s assume our user types the stringls -l,
a command/option pair that will print the contents of the current working directory to the standard output in long format.
getline
passes the string into a temporary buffer. The buffer is then parsed one word (token) at a time, each being stored separately as one element in an array of character arrays (aka an array of strings). The first element in our ls -l
example is the token “ls” and the second, “-l”.
After “tokenization,” the shell searches through a series of directory locations in an attempt to locate the program that executes the “ls” command.
1. Alias file(s) are checked to see if a shortcut for the command has been previously set. If the token command is found in the alias list, it is replaced in the string array by the actual command. To see what is in the alias list, type alias
at the shell’s command prompt.
GoS@ubuntu:~$ alias
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l='ls -CF'
alias la='ls -A'
alias ll='ls -alF'
alias ls='ls --color=auto'
2. Next, the shell searches its own built-in functions to see if the token command’s program exists. cd
, for example is a built-in function used to change directories. In our example, however, ls
will not be found as a built-in.
3. Therefore, the shell will continue it’s search in the PATH, a list of directories whose content programs can be executed no matter where in the directory tree it is run. You can view which directory paths are in the PATH
environmental variable by typing the following at the command line.
GoS@ubuntu:~$ env | grep PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin:/snap/bin
Since ls
is found in the the PATH, a child process must be created alongside the parent process so that the shell can handle order-of-execution properly. This branch from the parent process is created by making a call for the operating system’s kernel service, fork
.
In the forked child process, the tokenized command line arguments,ls
and> -l
are passed to another system-call, execve
. The -l
token is an optional argument that asks the system-call to output the directories contents with an extended set of dir/file information (permissions, size, etc.).
Because the parent process was already running at fork time, it will continue executing operations alongside the child process. This is an issue because we don’t want the command line to print until the child process’s execve
system-call has completed. This is resolved by making a system-call for wait,
effectively pausing parent operations until the child process exits.
This fork-wait-execute chain of system calls allows for an orderly output of directory contents, followed by the command prompt.
Had ls
been another command that was found neither in the built-ins nor in one of the PATH directories, the shell would have then checked to see if the command was an executable file in the current working directory. If it was found locally, the fork-wait-execute chain would have been implemented here. Otherwise, the shell would report via the standard error output stream that the given command does not exist.
Once ls
has finished executing and the prompt has been printed, the shell will call the getline
function again, waiting for the user to enter the next command string.
co-author: Jared heck