What happens when you type ‘ls *.c’ in bash?

Bryan Yep
6 min readOct 3, 2022

--

One of the most used commands in Linux is ls(short for ‘list’) which displays a list of the files and directories in the current working directory.
There are a few tricks of the hat for this command, like the -aoption that displays even the hidden files and directories, or the-l option that displays a long format of each item including permissions, owner, size, and other relevant data. Nonetheless, in this article we are going to focus on one particular syntax of the command ls, which is : ls *.c .

But before we get into topic, we need to talk about the PATH.

What is the PATH in Linux?

PATH, written with uppercase, is a environment variable (a.k.a. global variable) in Linux (and other operating systems derivated from Unix) that contains a list of directories to search for executable files. This is the variable that is used to know where to look when you type a command.

Lets see an example of what the environment variable $PATH contains with echo:

We see the following:

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

This is actually a list of directories, separated by colon (:). Then the list of directories in PATH without the colon would look like this:

/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin

This means that when we type the lscommand, the shell looks into the directories listed in the PATH to know where the executable file for the required command is stored so it can be found and executed. Excellent.

So, how do we know where exactly is the executable file for ‘ls’?

We can find that out with the command which ls:

We get the following directory:

/usr/bin/ls

And, as we can corroborate, it is actually listed in the PATH global variable displayed above. So now we know for sure the executable file for ‘ls’ is in the directory /usr/bin/ls listed in the PATH.

For summary, when we type lsinto the shell and press Enter, the shell first checks out the global variable PATH to know in which directories to look for that executable file, then it travels through the different directories looking for the ‘ls’ file, and finally it finds it in usr/bin and executes it. Beautiful. Pure magic.

  • It should be mentioned that the shell first looks if the command typed matches any alias before checking the PATH for built-ins and commands, then it checks if the first word of the command is a built-in from the shell itself, and finally it looks into the PATH directories for external commands. lsis actually an external command, that is why it exists in a directory listed on the PATH global variable.
    This is very important to know in case you have an alias named the same as a built-in or external command. For example, if you create an alias to rm * with the name lsand then execute ‘ls’, it will first respond to the alias and will execute the rm *command, and that will delete all the files from your current working directory. So please, don’t do that. Love yourself .
  • What is an ‘alias’? Just in case you were wondering, an alias is a group of characters the user asigns to a certain command, so that you don’t have to type all the command again and again, but rather just its ‘alias’.
    Of course this is mostly used to abbreviate long commands into short words that are easier to type in a keyboard. To create an alias, you just need to type the following command: alias -p name="value"
    where ‘name’ is the alias and ‘value’ is the command you want to shorten. We tend to use something similar on a daily basis. For example, you are probably thinking ‘tl;dr’ as you read through this article… But please don’t give up yet!

Now that we are getting deep into the topic, let’s talk about that *character in the ls *.c command. In order to understand this, we need to first know about ‘expansions’.

What are expansions?

As we have seen, when we type a command and press Enter, the shell takes several steps before actually executing the requested command.
One of this steps is reading the ‘expansions’ from the input.

What does this mean? It means exactly what it sounds like.
We type something into the shell, a symbol, a group of symbols, a certain syntax of symbols, and then the shell ‘expands’ those symbols, interprets them, and then outputs something completely different.

Let’s say, for example, the shell is like a child who likes to keep secrets, so he has certain ‘code words’ for different concepts. Let’s say we want to tell this child that our teacher smells really bad, but we don’t want the teacher to find out. So maybe, instead of saying ‘the teacher smells horrible’ we would tell him ‘the T smells like SH’. And then the kid laughs.

In this case, this hypotetic child ‘expanded’ the T for ‘teacher’ and the SH for ‘shoes’, respectively. He replaced those symbols in his mind because he knew the code words beforehand. That is how expansions work in bash. We just need to know the right codes so the shell can understand us and ‘expand’ those codes.

In this case, the *character is actually a wildcard, which are going to be interpreted as what is called a ‘Pathname Expansion’.

Then the wildcard *means: ‘any character or group of characters’.

The following is a brief list of wildcards and their expanded meaning:

As we can see on the image above, if we precede the ‘*’ wildcard with another character, the shell will look for all the files that begin with that specific character. (For example, g* means ‘any file that begins with ‘g’.)
The same applies if we do the opposite. If we write *g, the shell will look for all the filenames that end with a ‘g’.

I think you see where this is going!

The ‘ls *.c’ command. What does it do?

Yes, that’s right. If we follow the wildcard with ‘.c’, it will look for all the files that end with ‘.c’ in their names. It will actually display a list containing only the files and directories that match that criteria.

So now we can conclude that the command ls *.c will display a list of all the files and directories whose names end with the ‘.c’ extension in the curent working directory.

Let’s try this out:

As you can see, after displaying the output the shell prints the prompt again. This is common behaviour after finishing the execution of a command. The format of this prompt is actually dictated by the information stored in the PS1 global variable. You can check it out by typing ‘echo $PS1’:

Just one more thing before you leave!

Remember that the ls command lists files AND directories that match the specified criteria. So, if we had directories that for any given circumstance end with ‘.c’ the shell will actually display those directories too! Not only that, but it will also display the contents of those specific directories… Even if the files inside that directory don’t end with ‘.c’!

We’ve got to try this out:

This is just a little detail that can make a big difference.

Now you know how to use the ls *.c command and exactly how it works!

Thank you for reading to the end!

Happy coding! :)

--

--