So, the other day I decided to look around how a shell (like zsh) goes about executing commands. This time I had my
strace tool in hand, so I could snoop around what syscalls the shell makes while executing a command.
Note: This is basically me looking at this stack-overflow answer and drawing conclusions.
This the setup command I used.
Let’s look at this for a second, here. There are three parts to this command.
cat |. cat without arguments just echoes back whatever we write on the terminal. That means, whatever we write, we pass to
the command at the other end of the pipe strace as a whole. We will come to why we need this in a moment.
strace -f bash. This is simple, we just trace the bash process. The -f flag makes sure that all the processes created by
bash is also traced as well.
> /dev/null. We want to make sure that our screen is not clogged by the outputs of the command we run in addition to the
strace output. Hence we make sure that the output of bash is sent to null.
What I found
It starts off pretty much the same way all programs do
Quite interestingly, the output shows it blocking at the middle of the read syscall. Probably that is how strace
Reading a command.
What’s this? So, the shell reads commands one character at a time and not as a whole. This is exactly why we piped cat into
it. Otherwise, it would read every character we entered exactly when we entered it, making the flow difficult to understand.
Executing a shell builting
This part is pretty simple. Once it detects a builtin, it just executes it without further ado.
Executing any other command
This is much more interesting, we can actually see the shell going through the PATH variable looking for the command.
I have removed a lot of output for brevity’s sake but you get the gist of what’s going on.
Once shell reads the input and recognizes that it is not a builtin, it goes looking through the PATH environment variable
for the executable, in order. Once it finds it, it goes on with creating a child process for the executable and the usual process
Executing a non existent command
Another interesting thing happens when the command is not found.
The shell still creates a new process to inform you that the command does not exists.
This is a pretty simplistic view of the shell on the basis of how it interacts with the kernel. This however does not show
what logic that it internally uses. We need some different methods to analyze that.