Bash 201: Intermediate Guide to Bash


Last week we took a look at some of the basics of using the GNU Bash shell. Of course, that’s not all there is to getting to know your way around the shell.

Here we’ll take a look at configuring bash, setting aliases, and a few other tips and tricks for getting to know your shell. Learning the way around the shell is a marathon, not a sprint, so we’ll try to take it in small manageable chunks.


Configuring Bash

As mentioned in the previous tutorial, bash has several configuration files it reads when starting up, depending on how it’s starting. If starting as the login shell, bash will read the following in order:

  • /etc/profile
  • ~/.bash_profile
  • ~/.bash_login
  • ~/.profile

If bash is starting as a non-login shell, for instance when you open a terminal emulator like Konsole or GNOME Terminal, then it looks at the following files:

  • /etc/bash.bashrc
  • ~/.bashrc

A quick reminder, the tilde (~) is shorthand for your home directory. So ~/.bashrc expands to /home/user/.bashrc when interpreted by the shell.

To make modifications to the shell, then, you’ll want to modify ~/.bash_profile to change the login shell and ~/.bashrc for the non-login shell.

Let’s look at a simple bash configuration file:

# Set your favorite editor here

export EDITOR=/usr/bin/vim

# Set aliases

if [ -f ~/.bash_aliases ]; then 

    . ~/.bash_aliases


The lines prefixed with hashes (#) are comments. The shell will ignore those. The next line sets the EDITOR environment variable to vim. Bash looks at this to see what editor to use by default.

As explained a bit more below, the next lines tell bash to “source” a file to read aliases if the file .bash_aliases exists. It probably looks a bit cryptic, but the line “. ~/.bash_aliases” means “source ~/.bash_aliases”.

The PATH to Commands

When you type a command at the shell, bash looks in specific directories to see if it can find the command. So if you type ls at the shell, bash will only execute the command if it’s in its path, and will fail if the command isn’t found in one of the directories that are part of the path. If there’s a match in more than one of the directories in the path, bash picks the first one.

You can find out what your path is by running:

echo $PATH

You should see something like this:


Each directory where bash looks is separated by a colon. Notice that the first directory bash will check is the “bin” directory in my home directory, then /usr/local/bin, /usr/bin, and so on. If you’re logged into your system as root, you should see a different set of directories including /usr/local/sbin, /usr/sbin, and so on.

You can add directories to the path or change the path by editing your ~/.bashrc and ~/.bash_profile, like so:


Let’s break that down a bit. PATH= tells bash that it’s going to be setting your path, and then $HOME/bin tells it to add your bin directory, and then $PATH tells bash to add whatever is already in the path. The : is a separator between the entries.

You might already have something like this in your ~/.bash_profile and/or ~/.bashrc:

if [ -d "$HOME/bin" ] ; then



That tests to see if the bin directory exists in your home directory, and then adds it to the path. We’ll cover conditional statements and simple scripting in another tutorial, as it’s a bit beyond the scope of this tutorial.


An alias is a string that bash will swap out for another command. A simple example:

alias ls='ls --color=auto'

This tells bash “when I run ‘ls’ I actually want to run ‘ls –color=auto’, but don’t feel like typing all that.

You can set aliases at the shell prompt, and that’s good for testing, but it makes more sense to set the aliases in your ~/.bash_profile and ~/.bashrc. Or, create something like ~/.aliases and call that from your configuration files like so:

if [ -f ~/.bash_aliases ]; then 

    . ~/.bash_aliases


To see the aliases that are already set, just run alias at the shell. You might be surprised what’s already set by default on your distribution. Here’s the default set I have on openSUSE 11.2, without having made any changes:

alias +='pushd .'

alias -='popd'

alias ..='cd ..'

alias ...='cd ../..'

alias beep='echo -en "07"'

alias cd..='cd ..'

alias dir='ls -l'

alias l='ls -alF'

alias la='ls -la'

alias ll='ls -l'

alias ls='ls $LS_OPTIONS'

alias ls-l='ls -l'

alias md='mkdir -p'

alias o='less'

alias rd='rmdir'

alias rehash='hash -r'

alias unmount='echo "Error: Try the command: umount" 1>&2; false'

alias you='if test "$EUID" = 0 ; then /sbin/yast2 online_update ; else su - -c
 "/sbin/yast2 online_update" ; fi'

As a test, you might want to see what’s already been set by your distro and try to make up a few of your own.

Shell Expansion

Before bash executes a command, it evaluates the input and “expands” aliases and other expressions when appropriate. You’ve already seen a few examples of expansion with aliases and the tilde (~) expanding to $HOME.

Bash also has a few characters it expands, often referred to as “wildcards,” that it will expand to match one or more characters. This kind of pattern matching can be really useful. For example, if you want to see all the JPEGs in a directory, run ls *jpg (assuming your JPEGs are labeled with jpg, of course) and bash will expand that to match any filename that ends with jpg.

If you only want to match a single character, use ? instead. It will match a single character–so “ls ?.jpg” would only match filenames like “1.jpg” or “a.jpg” and not “01.jpg” and so on.

Bash also allows you to match ranges of characters with brackets. Using [abc12] would match one of those characters when expanded. So, running “ls -lh [abc12].jpg would match a.jpg, b.jpg, c.jpg, 1.jpg and 2.jpg, but not 12.jpg or abc.jpg.

You can also negate the pattern by prefixing it with a “bang” (!) like so: ls -lh [!ab12].jpg would match c.jpg or 3.jpg, but not 2.jpg, and so on.

Another handy trick is brace expansion. I like to organize my documents by year and date. So under my ~/Documents folder I have a 2010 directory with subdirectories 01 through 12. I don’t feel like typing out 01 through 12, so I make bash do the work instead:

mkdir 2010/{01..12}

When bash reads that it expands what’s in the braces to 01 through 12. This also works with letters, so if you wanted a through z subdirectories, you could run

mkdir {a..z}

and bash would create a through z directories. Can also work with uppercase letters.

This also works with comma-separated values. Say you have a directory full of image files in JPEG, PNG, and GIF format. You want to move all of them into an image directory. One way would be to type mv *.jpg *.png *.gif /images and that works OK. Another, slightly easier way, would be to use mv *.{jpg,gif,png} /images.

Another useful expansion is command substitution. This can be really useful if you want to include the results of a command in another command. The syntax is:

command `anothercommand`

Bash will run “anothercommand” and then execute the first command on the results. As an example, if you wanted to create a filename based on the output of the current date, you could use this:

touch `date +%F`.txt

The result of “date +%F” would be the year, month, and then date separated by the “-” character. So I’d get something like “2010-01-14.txt” as a new file.

More to Come

For now, that’s enough to take a step to the “next level” of using the shell. There’s much more to learn about working at the shell, but by now you should have a bit more comfort exploring and be able to do more at the shell. Future tutorials will cover some basics of shell scripting and useful commands to run at the shell.