3180 words
16 minutes
Bash Shell
2021-11-07
2022-11-07

Bash is the de facto shell on most widely used systems: Linux, macOS, and WSL on Windows 10/11.

Note: Starting from macOS version 10.15 Catalina, the default shell is zsh, not bash.

There are historical reasons why bash became the most popular shell in the world. Back in 1989 when it was first released, the technology world was very different. At that time, much of the software in the UNIX world had closed source code. Unix itself had patents and closed source code.

To use a UNIX system, you had to use a shell.

The most popular shell at that time was the Bourne Shell (sh), available under the command /bin/sh. It was called “Bourne Shell” because its creator was Steve Bourne.

Richard Stallman, with the GNU Project (and later Linux), almost revolutionized everything, starting with the Open Source revolution. The GNU Project needed a shell, and with the help of the Free Software Foundation, Bash was born. Inspired by the Bourne Shell, Bash stands for Bourne Again Shell, and it became a key component of the GNU Project, and one of the most popular software still in use today.

Bash can run scripts written for sh, which was a must-have feature for adoption, and it also introduced many new features not found in the Bourne Shell. From day one, it offered a great user experience and has seen many improvements since then. In this blog, we will discuss the most popular and useful features of Bash that you can try.

Getting Started with Bash#

Since Bash is the default shell on every system, the first thing you need to do to start using the bash shell is:

  • Log into the system if it’s a server
  • Open a terminal if it’s a laptop/computer

When you first open the terminal, you will see a prompt (usually $ or #).

How do you know if your shell is bash? Try typing the command echo $SHELL in your terminal. If you get /bin/bash, then you are using bash, or try typing help and press enter.

See? We just asked bash to execute the help command. This command will display the version of bash you are using, as well as a list of available commands in bash.

More often than not, you will never use the list of available commands in bash help, unless you are writing bash scripts. But, it will be useful if you want to know what you can do in bash.

99% of the time, you will use bash to navigate files and directories and run commands like ls, cd, and other common utility commands.

To navigate the file system, you will use the ls command, which is available under /bin/ls. Since bash has the /bin folder in $PATH, you can run the ls command without typing /bin/ls.

ls is the command to list the contents of a directory. If you do not specify a directory, it will list the contents of the current directory.

You usually start in the /home or /Users folder on macOS. My home folder is currently /home/memet.

memet@zxce3 ~ $ ls
Desktop  Documents  Downloads  Music  Pictures  Public  Templates  Videos

To navigate to another directory, you can use the cd (change directory) command. If you want to go back to the previous directory, you can use the cd - command.

memet@zxce3 ~ $ cd Desktop
memet@zxce3 ~/Desktop $ cd -
memet@zxce3 ~ $ 

cd .. will take you to the parent directory, and cd ../.. will take you two levels up.

memet@zxce3 ~ $ cd ..
memet@zxce3 /home $ cd ../..
memet@zxce3 / $ 

Depending on your Bash configuration, you will see the current folder in the prompt (symbol $). If you are using a simpler prompt, you can use the pwd command to see the current directory.

memet@zxce3 ~ $ pwd
/home/memet

pwd stands for print working directory

Command Editing#

When you type a command in the terminal, you can use several keyboard shortcuts to edit the command you typed. You can use the arrow keys to move the cursor, and the backspace key to delete characters before the cursor. If you want to delete the entire command, you can use the Ctrl + u command. Pressing enter will execute the command you typed.

This is normal and acceptable, but something that wowed early UNIX/Linux users.

Keyboard shortcuts help you edit the command you typed, and this is very useful when you want to edit a long command without using the arrow keys.

  • Ctrl + a will move the cursor to the beginning of the command
  • Ctrl + e will move the cursor to the end of the command
  • Ctrl + f will move the cursor one character to the right
  • Ctrl + b will move the cursor one character to the left
  • Ctrl + k will delete all characters after the cursor
  • Ctrl + u will delete all characters before the cursor
  • Ctrl + d will delete the character in front of the cursor

There are many more keyboard shortcuts you can use to edit the command you typed.

Autocomplete#

One of the most useful features in bash is autocompletion. It helps you type commands quickly and avoid typos. Try typing cd Doc and pressing tab. Bash will complete the command for you. If there are multiple options for the characters you typed, you can type more characters and press tab again.

memet@zxce3 ~ $ cd Do
Documents/  Downloads/
memet@zxce3 ~ $ cd Doc
memet@zxce3 ~ $ cd Documents/
memet@zxce3 ~/Documents $ 

Autocompletion also works for file and directory names. Try typing ls Doc and pressing tab. Bash will complete the directory name for you.

memet@zxce3 ~ $ ls Doc
Documents/  Downloads/
memet@zxce3 ~ $ ls Documents/
image.png  image2.png  image3.png

Autocompletion also works for commands

Shell Commands#

Using the shell, we can run commands available on our system. We can prefix commands with the full path (e.g., /bin/ls or /usr/bin/ls to list files and directories), but the shell has the concept of path, so we just need to type ls, and it will look for the command in the specified paths.

Commands accept arguments. For example, ls /bin will list all files in the /bin folder.

Parameters start with a dash (-) and can be combined. For example, ls -a will list all files, including hidden files (those starting with a dot .). ls -l will list files in a detailed format.

Commonly Used Commands#

There are many built-in commands on all systems, and they vary depending on whether it’s Linux/Unix or macOS or even the Linux distribution you are using.

However, let’s cover the most common and widely used ones.

When you encounter problems, such as not knowing a command or how to use it, you can use the man command to view the documentation for that command.

Here is a list of commonly used system commands:

CommandDescription
lsList files and directories
cdChange the current directory
pwdPrint the current directory
mkdirCreate a new directory
mvMove or rename files or directories
cpCopy files or directories
rmRemove files or directories

Every file in Unix has permissions, and you can use the chmod command to change them (not covered here). The chown command changes the file owner.

cat, tail, and grep are very useful commands for files. cat displays the contents of a file, tail shows the end of a file, and grep searches for a string in a file.

find and locate are commands to search for files. find searches for files in the current directory, and locate searches for files across the entire system.

tar and gzip are commands to compress files. tar compresses files, and gzip compresses already compressed files.

pico, nano, vim, and emacs are commonly installed and used editors.

whereis and which are commands to find the location of commands. whereis finds the location of commands, and which finds the location of commands that will be executed.

There are many more commands you can use on your system, and these are some of the commands you will likely use frequently.

Running Commands#

The cd and ls commands, as explained above, are found in the /bin folder. You can execute any file as long as it is an executable file by typing the full path, for example, /bin/pwd. Commands do not require the /bin folder, and you can execute files in the current folder with the ./ path indicator.

For example, if you have a file yamete in the /home/memet/scripts folder, you can run:

cd /home/memet/scripts

Then run ./yamete to execute the file.

Or you can run /home/memet/scripts/yamete from Bash, regardless of the current folder.

Jobs#

When you run any command, if it is a long-running program, your shell is taken over by that program. You can stop the command by pressing ctrl + c.

You can run commands in the background by adding & at the end of the command. For example, sleep 1000 & will run the sleep command for 1000 seconds in the background.

You can see a list of running commands with the jobs command. You can stop commands with the kill and killall commands.

memet@zxce3:~$ sleep 1000 &
[1] 1234
memet@zxce3:~$ jobs
[1]+  Running                 sleep 1000 &
memet@zxce3:~$ kill 1234
memet@zxce3:~$ jobs
[1]+  Terminated              sleep 1000 &

Background commands will stop if you close your shell.

Another useful command is ps to see a list of running processes.

memet@zxce3:~$ ps
  PID TTY          TIME CMD
  123 pts/0    00:00:00 bash
  456 pts/0    00:00:00 ps

And top to see a real-time list of running processes.

A job process can be stopped using kill <PID>.

Command History#

Pressing the up arrow key will show the commands you previously ran. This is a shell feature. Pressing the down arrow key will help you navigate back to the most recently run commands. Pressing enter will run the command again.

This is quick access to the commands you previously ran. Running the history command will display a list of all the commands you previously ran.

memet@zxce3:~$ history
    1  ls
    2  cd /home/memet
    3  ls
    4  cd /home/memet/scripts
    5  ls
    6  history

When you start typing a command, Bash can autocomplete the command you previously ran by referencing the commands you previously ran. Try pressing esc and then tab.

Honestly, I found the implementation of the fish shell to be better and easier. Maybe I’ll discuss it later.

Setting the Default Shell#

There are many shells besides Bash, such as Fish, Zsh, TCSH, and others. Each user on the system can choose the shell they want. You can change the default shell with the chsh command.

memet@zxce3:~$ chsh
Changing shell for memet.
Password:
New shell [/bin/bash]: /bin/zsh
memet@zxce3:~$ echo $SHELL
/bin/zsh

Customizing Bash#

As mentioned earlier, you may (or may not) see the current directory in the bash prompt. Where is this determined? In the Bash configuration file!

There is some confusion here because Bash uses different configuration files for different scenarios, and it also reads multiple configuration files.

Let’s discuss this confusion one by one. First, there is a big difference between whether Bash is initialized as a login shell or a non-login shell. A login shell means that the system is not running in a GUI (Graphical User Interface) and you log into the system through the shell. This is in the context of a server, for example.

In this case, Bash reads the /etc/profile configuration file.

And look in the user’s home folder, there are configuration files.

~/.bash_profile
~/.bash_login
~/.profile

Where ~ is the user’s home folder (this is automatically translated by Bash).

This means that if there are .bash_profile, ~/.bash_login, and ~/.profile files in the user’s home folder, Bash will read these files. If there are .bash_profile and ~/.bash_login files in the user’s home folder, Bash will read the .bash_profile file first.

If Bash is run as a non-login shell, Bash will read the ~/.bashrc configuration file.

Environment Variables#

Sometimes you have programs that use environment variables. These are values that can be set outside the program, and the program can access them. An API key, for example. Or a path you need to run the program.

You can set an environment variable with syntax like this.

VARIABLE_NAME=VALUE

The value can contain spaces and special characters by using quotes.

VARIABLE_NAME="THIS IS THE VALUE"

Bash scripts can use variables with the syntax $VARIABLE_NAME.

memet@zxce3:~$ VARIABLE_NAME="THIS IS THE VALUE"
memet@zxce3:~$ echo $VARIABLE_NAME
THIS IS THE VALUE

Bash also provides some variables you can use, such as:

  • $HOME for the user’s home folder
  • $PATH for the path accessible by programs
  • $SHELL for the currently running shell
  • $USER for the username

You can see the contents of these variables using the echo command.

memet@zxce3:~$ echo $HOME
/home/memet
memet@zxce3:~$ echo $SHELL
/bin/bash

Special Environment Variable: $PATH#

$PATH is a variable that contains a list of paths accessible by programs. The shell will look at this when you type a command. Folders are separated by colons : and are ordered from left to right - Bash will look at the first one, search for the command there, and if not found, look at the second one, and so on.

My current path is like this.

memet@zxce3:~$ echo $PATH
/usr/local/sbin:/usr/local/bin:/sbin/:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/games:

You can edit this in the ~/.bashrc file by adding the path you want.

PATH=$PATH:/home/memet/scripts

Aliases#

Aliases are shortcuts for commands you frequently use. For example, you can create an alias for git status with gst.

memet@zxce3:~$ alias gst='git status'
memet@zxce3:~$ gst
On branch master
Your branch is up to date with 'origin/master'.

To define an alias, use the syntax like this.

alias <alias>=<command>

If you want to create an alias for a long command, you can use quotes.

alias <alias>="<command>"

You can also define aliases in the ~/.bashrc file.

Be careful with quotes if you have variables in the command: using double quotes will resolve the variable at definition time, using single quotes will resolve the variable at call time. These are different.

alias lsnow="ls $PWD"
alias lslater='ls $PWD'

$PWD refers to the current folder, and if you type lsnow and lslater, you will get different results. lsnow will display the contents of the current folder, while lslater will display the contents of the folder where you defined the alias.

Advanced Command Line Features#

Wildcards#

ls and many other commands can use wildcards, you can list all files starting with a with the command ls a*.

memet@zxce3:~$ ls a*
a.txt ab.txt  abc.txt

Or you can list all files ending with .txt with the command ls *.txt.

memet@zxce3:~$ ls *.txt
a.txt ab.txt  abc.txt

Or all files ending with b or c with the command ls *b* *c*.

memet@zxce3:~$ ls *b* *c*
a.txt ab.txt  abc.txt

Redirecting Output and Standard Error#

By default, commands started in the shell send both output and error to the terminal. This may not be what you want, you can specify where the output should go.

Actually, to /another file/, because in Unix even the screen is considered a file. Specifically:

  • 0 is standard input
  • 1 is standard output
  • 2 is standard error

You can redirect standard output to a file by adding 1> at the end of the command, followed by the file name.

Using the same technique, you can redirect standard error to a file by adding 2>.

There is a shortcut > for 1>, as it is used the most.

Example:

ls 1> output.txt 2> error.txt
ls > output.txt 2> error.txt

Another shortcut, &>, redirects /both/ standard output and standard error to the same file.

ls &> output.txt

Another common thing you might do is redirect both standard output and standard error to /the same/ place. You can use 2>&1 to redirect standard error to the same place as standard output.

ls 1> output.txt 2>&1

Running Commands in the Background#

You can tell Bash to run a command in the background without taking control of the shell by adding & at the end of the command.

memet@zxce3:~$ top &
[1] 1234

top is a command that displays a list of running processes on the system.

[1] 1234 is a message indicating that the command is running in the background, and the process number is 1234.

An application that usually takes control of the shell is now running as if nothing happened. You can bring it back to the shell by typing fg (foreground), but now we are entering the realm of processes and jobs, which is a big topic on its own. Here is a brief overview:

When a command is running, you can use Ctrl + Z to pause the command and move it to the background. The shell returns to the foreground, and you can now run bg to resume the paused command in the background.

When you are ready to return to it, you can use fg to bring the command back to the foreground.

You can also see a list of running background processes with the ps command, and the list shows all process pid numbers. Using the pid of the paused process, you can bring the process back to the foreground with the fg <pid> command. The same applies to bg.

memet@zxce3:~$ ps
  PID TTY          TIME CMD
 1234 pts/0    00:00:00 top
 1235 pts/0    00:00:00 bash
 1236 pts/0    00:00:00 ps
memet@zxce3:~$ fg 1234
top

Command Queue#

You can ask Bash to run a command after another command finishes by separating them with a semicolon ;.

memet@zxce3:~$ sleep 5; echo "done"
done

You can repeat the command queue on the same line.

Redirecting Output#

Programs can receive input from any file using the < operator, and redirect output to another file using the > operator.

memet@zxce3:~$ echo "hello" > output.txt
memet@zxce3:~$ wc < output.txt
1 1 6 output.txt

wc is a command that counts the number of words, lines, and characters in a file.

Pipe#

A pipe is an operator that directs the output of one command to the input of another command. It is a very effective way to process data. It is similar to the > operator, but more flexible. Use the | operator to combine commands. For example, wc gets input from the output of echo hello and counts the number of words, lines, and characters in the string.

memet@zxce3:~$ echo "hello" | wc
1 1 6

Command Grouping#

Use && to combine two commands with the word “and”. If the first command succeeds, the second command will run. If the first command fails, the second command will not run, and so on.

memet@zxce3:~$ false && echo "success"
memet@zxce3:~$ true && echo "success"
success

Use || to combine two commands with the word “or”. If the first command fails, the second command will run. If the first command succeeds, the second command will not run, and so on.

memet@zxce3:~$ false || echo "success"
success
memet@zxce3:~$ true || echo "success"
memet@zxce3:~$

The ! operator reverses the value of a command.

memet@zxce3:~$ !true
false
memet@zxce3:~$ !false
true

You can also use parentheses to group expressions to avoid confusion and change the execution priority.

memet@zxce3:~$ (true && false) || true
true
memet@zxce3:~$ ! (true && false) || true
false

Shell Script#

One of the best features of shells like Bash is the ability to create programs that can be run in the shell, essentially automating command execution.

I will create several blogs about this later, which will be divided into several tutorials. Why not combine them into one? Because the topic is much deeper and longer.

A brief introduction: a script is a text file that starts with a line indicating that it is a shell script (and what shell it needs), and then contains shell commands to be executed.

#!/bin/bash
echo "hello"

You can save this as a script file and make it executable with the chmod +x script command. Then you can run the file with the ./script command (./ is the current file location).

Shell scripting is actually beyond the scope of this tutorial, but I want you to know about it. So I will create some tutorials on shell scripting later. Scripts can do many things, such as taking input from users, processing data, and much more.

Scripting strategies also work in many other shells, such as zsh, fish, and csh.

Example fish shell script

#!/usr/bin/fish
echo "hello"

Please leave comments and suggestions for this blog. Thank you.

Bash Shell
https://zxce3.net/posts/shells/bash-shell/
Author
Memet Zx
Published at
2021-11-07