Introduction to the Linux/Mac Command Line (with bash and zsh)

An introduction to the command line interface used on Linux, macOS, and the Windows Subsystem for Linux (WSL).

Published 2024-12-30
40 minutes completion

What is the Linux/macOS Command Line?

If you are used to using a mouse and keyboard to interact with your computer, you may not be familiar with the command line interface (CLI). The command line is a text-based interface for interacting with your computer's operating system. Think of it as a text-based way to do things you normally do with your mouse and keyboard, like managing files or running programs.

If you found this blog using a search engine, it probably means you have already found your way to a command line yourself and are looking to use it, whether you are running Windows with the Windows Subsystem for Linux (WSL), using a Mac or Linux machine with a terminal, or remotely using a Linux Virtual Machine in the cloud or at your place of work. Using the command line can be intimidating at first, but once you get the hang of it, you'll find what a powerful way to interact with your computer, even if it is not the primary interface to interact with your own computer.

What is a Shell?

A shell is a program that interprets your commands and executes them. It is the interface between you and the operating system. The most common shell is bash (Bourne Again Shell), but there are many others, including zsh (Z Shell), fish, and tcsh. This guide will focus on bash and zsh, which are largly compatible and there is a good chance one of these will already be set up as your default shell, so when you open up a terminal window, you are probably already using it.

Which shell am I using?

Lets begin with our first command. Open up a terminal window and type the following command:

echo $SHELL

As you follow along, you should press Enter/Return after each command.

This will print out the path to the shell you are using. It will likely begin with /bin/ or
/usr/bin/ followed by bash or zsh, and if it does, you are good to go. If it does not, you can change your shell to bash or zsh by running one of the following commands:

/bin/bash or /bin/zsh

This will take you into bash/zsh so you can follow along with the rest of this guide. It is possible to change your default shell to bash or zsh but it may be wise to wait until you are more familiar with the command line before doing this.

Basic Navigation and File Management

In this section we will be using the following commands:

  • ls - Lists files and folders in current directory
  • mkdir folder_name - Creates a new directory
  • cd directory_name - Changes to another directory
  • pwd - Shows your current location ("Print Working Directory")
  • touch file.txt - Updates a file timestamps/creates a new empty file

Type ls to see a list of files and folders in the current directory. You may have visible files in there already, otherwise nothing will be shown.
Lets create a directory so we can use to keep all our files in one place for the rest of this guide.

Type mkdir shell_introduction to create the directory and ls again to see it in the list.

Your output should be similar to this, although you may had additional files and your prompt (the $) may have additional text:

[NO_COPY]
$ mkdir shell_introduction
$ ls
shell_introduction
$

Now we can change to that directory with the cd command.

cd shell_introduction

Now we are in the shell_introduction directory and we can create a file in it.

If you ever need a reminder of what directory you are in, you can use the pwd command,try it.

Now type touch file1.txt to create an empty file, and then type ls to see the file in the directory.

The touch command is actually used to update the timestamps of a file but it is often used to create an empty file, it is perhaps used more often for this purpose than for updating the timestamps so it is a good to remember. We chose to use touch here but there are actually a number of ways to create an empty file, there are often many ways to acheive the same goal when using the command line. The more commands and tricks you learn, the more efficient you will be.

Digging deeper into file system commands

Go ahead and create a few more files, and then use ls again to see them all.

$ touch file2.txt
$ touch file3.txt
$ touch file4.txt
$ ls

You can should see something similar to this:

[NO_COPY]
$ ls
file1.txt  file2.txt  file3.txt  file4.txt

The mv command is used to move or rename files, lets use it to rename file1.txt to file1_renamed.txt, and file2.txt to .file2.txt.
Notice the . in front of the .file2.txt file.

$ mv file1.txt file1_renamed.txt
$ mv file2.txt .file2.txt
$ ls

Now you should see this:

[NO_COPY]
$ ls
file1_renamed.txt  file3.txt  file4.txt

What's happened to .file2.txt? Well, files that start with a dot are hidden and will not be shown by default. You can use the ls -a command to see all files, including hidden ones.

$ ls -a

Now you should see something similar to this:

[NO_COPY]
.  ..  .file2.txt  file1_renamed.txt  file3.txt  file4.txt

So now we can now see our hidden file, but what are the . and .. files?

Lets add another argument to the ls command to see more details about each file, the l argument, for a long listing. We can combine it with the a argument to see hidden files too.

ls -al

Now you should see something similar to this:

[NO_COPY]
total 8
drwxr-xr-x 2 nick nick 4096 Dec 31 00:42 .
drwxr-xr-x 3 nick nick 4096 Dec 31 00:33 ..
-rw-r--r-- 1 nick nick    0 Dec 31 00:33 .file2.txt
-rw-r--r-- 1 nick nick    0 Dec 31 00:34 file1_renamed.txt
-rw-r--r-- 1 nick nick    0 Dec 31 00:33 file3.txt
-rw-r--r-- 1 nick nick    0 Dec 31 00:33 file4.txt

The first character of each line is a '-' to indicate a regular file, a d to indicate a directory.

When navigating the file system, . is the current directory and .. is the parent directory, it may be confusing at first to see these entries actually in the listing but you will see them when listing all the files in any directory.

The first column provides a lot of information about the attributes of the file, for now we just concentrate on the first character. A - indicates a regular file, a d indicates a directory.

Type the following:

$ pwd
$ cd .
$ pwd
$ cd ..
$ pwd
$ cd shell_introduction
$ pwd

pwd shows you what directory you are in, before and after each cd command and you should have seen cd . did not change the directory and cd .. went up one level to the parent directory.

mv can be used to move files as well as rename them. Lets move two of our files to a new directory...

mkdir new_directory
mv file1_renamed.txt new_directory
mv .file2.txt new_directory/file2.txt
ls new_directory

The first mv command moved file1_renamed.txt to the new_directory directory and the second mv command moved .file2.txt to the new_directory directory but with a new name without the dot prefix, so unhiding it.

Wth the last ls commmand, this time we specified a directory to get the listing for, so you should see the two files we moved:

[NO_COPY]
file1_renamed.txt  file2.txt

Lets clean up a bit before continuing. The rm command is used to delete files (and with certain parameters, directories too) and rmdir is used to delete empty directories.

We'll remove the files from the new_directory along with the directory itself. We could use cd to change to the new_directory directory and then use rm to remove the files but another approch is to specify the path to the files to remove.

$ rm new_directory/file1_renamed.txt
$ rm new_directory/file2.txt
$ rmdir new_directory
$ ls new_directory

On many systems, the rm and rmdir will not provide any output unless there is an error, such as the file not existing due to a typo. On other systems, with different configurations, you may be prompted to confirm the deletion of the files and directories.

The final ls command should show an error message as below because the new_directory no longer exists.

[NO_COPY]
ls: cannot access 'new_directory': No such file or directory

The echo command

Before stepping into more complex commands, here is a quick and each one, echo is used to print out a message to the screen.

$ echo "Hello, World!"

Try it. Perhaps not the most interesting but it will become very useful when we start combining commands.

Output redirection

Rather than printing to the screen, we can redirect the output of a command to a file. There are two output streams, standard output (stdout) and standard error (stderr).

First ensure you are in the shell_introduction directory typing pwd. If not, use cd ~/shell_introduction to change to it and then type pwd again to confirm.

Let's use redirection with two of the commands we have already used...

$ echo "File content" > file_content.txt
$ ls made_up_directory > made_up_directory_listing.txt

You should notice that the echo command didn't print anything, this is because the output was redirected to a file.

The ls command however still printed to the screen, an error message to say the directory didn't exist.

This demonstrates the two different streams. stdout is where out output normally gets sent, but error messages are being sent to stderr which are still being displayed. If you think about it, this makes sense, we want to capture the output to another file, but also want to see if any errors occurred rather than sending these to the file too.

However, we can, if we wish, capture error messages too:

$ ls made_up_directory 2> made_up_directory_error.txt > made_up_directory_listing.txt
$ ls

You should see the made_up_directory_error.txt and made_up_directory_listing.txt files amongst other files in the directory. Lets look at the contents of them:

$ cat made_up_directory_error.txt
$ cat made_up_directory_listing.txt

You should see the error message in the made_up_directory_error.txt file and the listing of the files in the made_up_directory_listing.txt file, of which there was none, so the file is empty.

Try using the cat command to also see the contents of the file we created with echo "File content" > file_content.txt

Now lets try something else:

$ echo "New content" > file_content.txt
$ echo "Even more content" >> file_content.txt
$ cat file_content.txt

The content returned by the cat command should be:

[NO_COPY]
New content
Even more content

Our use of > again, writing to the same file, overwrote what was already there. We then used >> which appends to the file, rather than overwriting it.

Standard Input (stdin) and Pipes

A powerful feature of the command line is the ability to pipe the output of one command to the input of another. Lets talk about about stdin.

  • stdout - standard output
  • stderr - standard error
  • stdin - standard input

By changing commands togather, you can take the input of one command and pass it to another. Looking at cat again, rather than passing a file to it, we can pass the output of another command to it. The pipe character (|) is used to connect the output of one command to the input of another. Try this:

$ echo "Hello, World!" | cat

This will print out the message "Hello, World!" to the screen. No different to the echo command alone so not particularly useful but it demonstrates the concept. Now try just cat alone:

$ cat

Type some text and press Enter/Return and you should see it echoed back to you. Cat is actually acting in the same way it did when used with the echo. It just reads the input from stdin and passes it to stdout. Without any other channing of commands or redirection, stdin is the console input and stdout is the console output so it just echoes back to you.

Onces you've finished, press Ctrl+D to end the input and return to the regular command prompt.

Lets use what we've learned for a new trick, we will use the cat command to read the contents of a file and pass it to another command. You may be able to guess what this will do as you try it. Make sure Ears has a capital E at the start, everything else should be lowercase. Add 5-10 of your own words after pears if you like.

cat > words.txt
Ears
hands
legs
apples
oranges
pears

The cat command read our words from stdin (the console). Press Ctrl+D if you have not done so already to complete the input.

As everything you typed is written to the file, you won't be able to go back and change anything if you make a mistake but don't worry,
just go ahead and do it again after you have pressed Ctrl+D if you want, remembering that > will overwrite the file.

Now type cat words.txt to see the contents of the file.

Now we have a file of words, we can see how we can chain commands to do interesting things.

$ wc -w words.txt
$ cat words.txt | wc -w

The wc command produces various counts of the contents on the file, we used -w to count the number of words. You can also try -l to count the number of lines and -c to count the number of characters.

We have used wc in two ways, directly and as a pipe to demonstrate that wc is another command that can take a filename as an argument or take the input from stdin. The result is the same.

Lets try something else:

$ cat words.txt | sort

This will sort the words, seemingly alphabetically but you will notice Ears before apples. This is to do with the way capital letters come before lowercase letters the way they are stored and sorted by the computer, so is just something to be aware of.

A very common command used by by people who use the command line is grep, which is short for global regular expression print. It is used to search for a pattern in a file or stdin. You can search through a file for very complex patterns, but for now we will just keep things simple.

Try this:

$ cat words.txt | grep "ears"

You'll see a result of pears, aren't we loking for ears? Well grep isn't matching just for whole words using it in this way, it's just matching the matching the sequence of letters and Ears case a capital E so it matches, so lets make the search case insensitive:

$ cat "words.txt" | grep -i "ears"

This time you should see both Ears and *pears" in the result.

Lets chain another command, instead of listing all the matched lines, we can count the number of matches:

$ cat "words.txt" | grep -i "ears" | wc -l

If you've followed along so from the beginning, you may remember me saying there are many ways to achive the same goal. Well we could have just typed this:

$ grep -i -c ears words.txt

A more concise way to achieve the same result, but that doesn't mean the other solution is wrong. The amount of things you can do with the command line is vast, you don't have to learn every command there is, but the more you learn, the more powerful it will become.

© 2025 Goldnode. All rights reserved.