We came across ls in a previous article when we explained “ What happens when you type ls *.c”. However in this article we’re going to explain what really happens when you type ls- l.
ls
is a Unix-related systems command that lists the contents of a directory. The -l
on the other hand is known as an option or flag. We can find multiple options for ls
, too many to list in fact, however the l
in particular will allow ls
to list the contents of a directory in “long format”. Whereas “normal format” will only list the names of each file and sub-directory, “long format” will write each file name on its own line, as well as include additional info such as owner’s name, permission settings, a date for when it was last modified and the size of the content
1. This is what you first see when you open the terminal:
2. Go ahead and type the commandls -l
:
3. you’ll end up with something like this when pressing ENTER:
So what really happens when you type “ls -l” in the shell?
the result of shell using getline()
to take from standard input and write the resulting character string to a variable of type char *
is what appears to be a prompt for user input. This character string needs to be split into its components, i.e., a command plus its options and arguments.
Before I discuss tokenizing or splitting what was entered into getline()
, allow me to take a detour. If getline()
does not fail, shell will create a child process for its current process using fork()
. Built-ins only need a fork to be executed. Otherwise, if the PATH
is searched through , a function execve()
will be then called to execute the external command. Let’s get back to our single string of command + options + arguments.
In the case of (ls -l
) a tokenizer function will divide the character string into two parts based on the fact of the separation of the components with a space. This is known as parsing or tokenizing.
expansion could be ignored with special characters like bracket, `$
`, and the asterisk (`*
`). the expansion will usually occur after tokenization however before the command is searched for.
Now that we separated the command from its option, we’re able to begin the search for the command, and the first place to do that is a file in the home directory whose path is ~/.bashrc
. If alias is not found, the command would be compared to the list of built-ins.
Built-ins are usually defined when the shell starts and undefined when it ends since they’re stored in memory allocated for the shell . Different shells store built-ins differently. Because they’re are built in the shell itself they’re much faster to load than if they were defined in an external file. In a project that this writer contributed to, built-ins were listed in a structure from which a function pointer could select the proper built-in function to execute.
Finally, when the command is not found in the list of built-ins, the shell will search a single string of different directories separated by colons called the environment variable PATh
. First it’ll be tokenized into its component directories then concatenated with a /ls
to create a bunch of hypothetical paths that enables the determination if these hypothetical paths exist by having the shell call a function stat()
. This function will make a comparison of these hypothetical paths with the actual paths, and if ls actually exists in one of them, stat
will return data that the command was found.
the shell will return an error If the command cannot be found in .bashrc
, the list of built-ins, or in PATH
.
A list of options will be present in that command’s source code wherever the command is found . The list will take the form of switch statements or a structure. and this is where you will find the -l
so this is what actually happens when we type ls -l
into our shell.