Department of Computer Science
Bash Tutorial

Video Tutorial (mp4)

Preface

The goal of this tutorial is to give the beginner a very basic familiarity with bash, the standard Unix shell of our department.  It is not comprehensive.  Covering only a small subset of commands and features, this should be considered a starting point.  Potential CS majors should pursue more knowledge of their computing and editing environments, assured that the cost of such learning will be more than compensated by time saved over the course of their years as a major.

Imagine not knowing how to cut and paste.  Imagine the tedium of deleting a section of a paper character by character.  Then imagine the tedium of retyping the section character by character in a different location.  Cut and paste are simple to learn, and save the user much time and effort.  Having a good command of one's computing/editing environment will aid one considerably as a programmer as well.

In this tutorial, we focus on the shell, where one interacts with the computer through textual commands.  While the drag, drop, and double-click of window-based interfaces to operating systems are sufficient for most users, a text-based interface offers surprising power to the serious user.
 
In the beginning...

If you're doing this tutorial as a class exercise, be sure to try everything shown for yourself.  Don't merely read it - do it!  There are instructions at the end of this tutorial for how to submit evidence of your work.  Plan to do this tutorial in one sitting so that all of your work is shown.

The first thing you need to do is get a terminal window with bash.  If you are logging in remotely (e.g. via MobaXterm SSH on the PC, or via XQuartz terminal SSH on the Mac), you will already start in a terminal window via SSH (secure shell). There are many ways to do this on different Linux systems with different Linux desktop environments.  Here is how we do it with our current desktop environment choices:

Close the terminal window by clicking the "X" in the upper corner.  (You can also close the window by typing Control-D at the command line.  I recommend learning keyboard shortcuts like this wherever you can.  The more you can substitute efficient keyboard-based commands for mouse-actions, the better.)  Now open another terminal window.  Repeat this a couple times until you're comfortable with it and have the process memorized. 

Now onto the shell!  The terminal window is fairly empty to start with.  You'll likely see only a prompt, the beginning of what is called the command line.  For instance, on the machine "cs1" your prompt might look like:

nuyuzr01@cs1:~$

If you're on one of our lab machines in Glatfelter 112 or 207, your machine name will have the form "cs##". The prompt serves to (1) give you information about your current situation, and (2) indicate that the shell is awaiting your next command.  In this case, the text before the ":" is your username, the at symbol (@), and the name of your machine or host.  The text between the ":" and "$" is your current working directory.  A directory is the same as a folder in window-based graphical user interfaces to operating systems.  Just as you can have files and folders in a folder, you can have files and directories in a directory.  The directory name "~" is shorthand for "home directory", the place where you store your files on this system.  After the "$" is the place where you type your next command.

The first thing you'll probably want to do is change your password.  Type the command "yppasswd" (short for yellow pages password), enter your current password one, and your new password twice.  You will not be able to see the passwords as you type them.  A good site for customizable password generation is xkpasswd.net.  Write this new password down and don't forget it!

nuyuzr01@cs1:~$ yppasswd
Changing NIS account information for nuyuzr01 on ada.
Please enter old password: (what you type here will not be seen, not even little circles to indicate hidden characters)
Changing NIS password for nuyuzr01 on ada.
Please enter new password: (what you type here will not be seen)
Please retype new password: (what you type here will not be seen)

The NIS password has been changed on ada.  

nuyuzr01@cs1:~$

Let's imagine you come upon an abandoned computer that's been left logged in and you want to know who is logged in.  From a terminal window, simply type the command "whoami" (all one word) and press the Return key:

nuyuzr01@cs1:~$ whoami
nuyuzr01
nuyuzr01@cs1:~$

This may seem useless given the prompt, but users can modify their prompts to exclude the username.  Suppose you want to see what time it is.  Enter the command "date".

nuyuzr01@cs1:~$ date
Thu May 25 16:00:57 EDT 2017
nuyuzr01@cs1:~$

As mentioned before, "~" is shorthand for your home folder.  Suppose you wish to know exactly where that is on our system with respect to the root directory "/".  The command "pwd" (print working directory) will tell you.

nuyuzr01@cs1:~$ pwd
/Accounts/turing/students/s20/nuyuzr01
nuyuzr01@cs1:~$

If ever you wish to clear your terminal window, enter the command "clear".  Try it.

nuyuzr01@cs1:~$ clear
(window clears and prompt appears at top)
nuyuzr01@cs1:~$

"whoami", "date", "pwd", and "clear" are simple commands.  There are many more commands and some can be very complex.   You'll remember the ones you use the most, but don't try or expect to remember every command you encounter.  It's more important to (1) know what commands are available, and (2) know where to find documentation for commands.  The command "man" is short for "manual" and will give you information about commands.  Enter "man pwd" to see the documentation of the command "pwd".

nuyuzr01@cs1:~$ man pwd
PWD(1) User Commands PWD(1)

NAME
pwd - print name of current/working directory

SYNOPSIS
pwd [OPTION]...

DESCRIPTION
Print the full filename of the current working directory.
...

In using "man", you can press space to page forward, "b" to page back, and "q" to quit.  If such man pages are difficult to understand, there are a variety of other sources of information about UNIX systems similar to this.  Check out the helpful links at the bottom of this page, or simply perform an internet search for the command name.

The bash command line makes it simple to re-execute previous commands.  Bash keeps a command history and allows you to easily select and edit commands from this history.  Enter "history" to see the commands you've executed so far.

nuyuzr01@cs1:~$ history
   1  yppasswd
   2  whoami
   3  date
   4  pwd
   5  clear
   6  man pwd
   7  history
nuyuzr01@cs1:~$

There are a variety of ways to access previous commands.  The simplest is to use the up and down arrows to move through previous commands until you find the one you're interested in.  Use the up and down arrows to review your command history and press Return when you find the "date" command.  Then look at your history again.  Remember that you don't have to type "history" out again.  Two up arrows and a return will suffice.  Alternate between execution of the commands "date" and "history" using the arrow keys and return only until you're comfortable with using this feature.

Most commands you type will contain more information that these.  What do you do if you find you typed the first character wrong only after you'd completed the last?  Command line editing is easy with bash.  You can use the left and right arrow to move the cursor and edit your command.  Type "hooami" and press return.

nuyuzr01@cs1:~$ hooami
hooami: command not found
nuyuzr01@cs1:~$

Now retrieve the previous command and edit it so that it correctly shows "whoami" and press Return.  Your cursor does not have to be at the end of the line to press Return.  There are many other tricks to editing the command line.  Bash uses "emacs-style" command line editing (e.g. Control-a = beginning of line, Control-e = end of line), so you are encouraged to learn how to use emacs editing commands and see which ones are applicable to bash command line editing as well.

Most commands have options which allow a single command to have many possible behaviors.  We will illustrate this with the command "ls" (list directory) which lists the contents of the current working directory.  First, type "ls".  "public_html" is the name of the directory where you can put web pages.  If you do not have a "public_html" directory, create one now with the "mkdir" (make directory) command: 

nuyuzr01@cs1:~$ mkdir public_html
nuyuzr01@cs1:~$

An HTML file "~/public_html/index.html" for user nuyuzr01 will have the web address "http://cs.gettysburg.edu/~nuyuzr01/index.html" on our system.  When using "ls" (list directory), a name ending with "/" indicates that it is the name of a subdirectory of the current directory.  In our current system, directories are instead indicated by blue-purple coloration by default.

nuyuzr01@cs1:~$ ls
Desktop Documents Downloads Music Pictures Public public_html Templates Videos
nuyuzr01@cs1:~$

However, there's more in your home directory than meets the eye.  Files that start with "." are hidden files.  You can list all these files using the ls option "-a".  Enter the command "ls -a" to see these files (your files will vary).

nuyuzr01@cs1:~$ ls -a
.              .cache   .dmrc      .ICEauthority  .pki         .thumbnails
..             .compiz  Documents  .local         .profile     Videos
.bash_history  .config  Downloads  .mozilla       Public       .Xauthority
.bash_logout   .dbus    .emacs.d   Music          public_html  .xsession-errors
.bashrc        Desktop  .gconf     Pictures       Templates    .xsession-errors.old
nuyuzr01@cs1:~$ 

What then are these odd hidden files "." and ".."?  These are actually special directories that point to directories.  "." points to the current directory.  ".." points to the directory the current directory is in, that is, the parent directory.

Now suppose you want to find out a lot more information about these files (e.g. size, date created, security info, etc.).  The option "-l" gives the long version of the directory listing.  Try it by adding the additional option "-a" onto the previous command as follows:

nuyuzr01@cs1:~$ ls -a -l
total 120
drwxr-xr-x 21 nuyuzr01 student20  4096 May 25 16:22 .
drwxr-xr-x 77 root     daemon     4096 Mar  9 10:48 ..
-rw-------  1 nuyuzr01 student20  82   May 19 17:00 .bash_history
-rw-r--r--  1 nuyuzr01 student20  220  Dec 21 09:53 .bash_logout
-rw-r--r--  1 nuyuzr01 student20  3486 Dec 21 09:53 .bashrc
...(skipping details)...

There's actually a shorter way to enter this command: "ls -al" is equivalent. 

nuyuzr01@cs1:~$ ls -al

Recall that one can make a new directory using the command "mkdir" (make directory).  We will begin by creating a directory "test" in our home directory ("~") with command "mkdir test".  Note that this is different than "Mkdir test" or "MKDIR test".  Most UNIX commands, options, etc. are case-sensitive.  That is, case matters.  There are circumstances where capital letters are used by convention, but since you'll be typing directory names often and it's easier to type lowercase letters, it's generally a good idea to keep directory names short, descriptive, and in lower case. 

nuyuzr01@cs1:~$ mkdir test

Next, we will create a subdirectory "subdir1" within that "test" directory.  The way one refers to a directory inside a directory is to refer to list directories top down separated by slashes ("/").  So directory "subdir1" within directory "test" would be referred to as "test/subdir1".  

nuyuzr01@cs1:~$ mkdir test/subdir1

The command "cd" (change directory) followed by a directory name changes one's working directory to that specified directory.  The command "cd" followed by nothing returns us to our home directory (~).  Let us now change into the "test" directory, list the contents of that directory, create a second subdirectory "subdir2" relative to that directory, create a third subdirectory "subdir3", remove (delete) that subdirectory with the command "rmdir subdir3" (remove directory), change back to our home directory, and list the test directory's contents.  The command "rmdir" will not remove a directory that is not empty.  Note that (1) the prompt changes as we change our working directory, and (2) the "ls" command lists the contents of the working directory if no directory is specified:

nuyuzr01@cs1:~$ cd test
nuyuzr01@cs1:~/test$ ls
subdir1
nuyuzr01@cs1:~/test$ mkdir subdir2
nuyuzr01@cs1:~/test$ mkdir subdir3
nuyuzr01@cs1:~/test$ rmdir subdir3
nuyuzr01@cs1:~/test$ cd
nuyuzr01@cs1:~$ ls test
subdir1  subdir2

One of the most great conveniences of the bash shell that will significantly reduce your keystrokes with practice is tab completion.  If you press the tab key while typing a command, bash will seek to complete it.  If you press the tab key while typing a directory or file name, bash will seek to complete it.  If there's an ambiguity, bash will complete as much as possible, but a second tab press will print the valid completions.  For example, suppose we wish to change directory into "test/subdir1".  Start by typing "cd t" and then press tab.  Bash will complete out to "cd test/", giving the option to stop with that completion.  Press tab again, indicating the desire to complete with more characters, and bash will complete out to "cd test/subdir".  At this point, there's an ambiguity.  Does the user want "subdir1" or "subdir2"?  But perhaps you're in a situation where tab completion has stopped and you don't understand the ambiguous choice that has halted completion.  Simply press tab again and the options are listed.  In this case, the shell prints "subdir1/ subdir2/" and prints your prompt and current command again, awaiting the completion of your command.  Type "1" and press enter with the fully formed command "cd test/subdir1".

nuyuzr01@cs1:~$ cd test/subdir
subdir1/ subdir2/
nuyuzr01@cs1:~$ cd test/subdir1

Now reflect on how few of those characters you actually typed.  Tab completion takes some getting used to.  Without deliberate practice, I see new users pass up this wonderful feature, and it effectively costs them dearly over time in excessive keystrokes relative to the learner that is open to learning the shell fully.  Use the command history.  Use tab completion.  You'll thank yourself for it later.

Next, let's create some plain text files.  The "touch" command followed by a filename will create an empty file if no such file exist, or, if it does exist, change the access and modification time as if you just accessed and modified it.  The "cat" command is short for "concatenate".  It concatenates given files and outputs them.  If no files are given, it reads characters from you in the terminal.  If we append "> test2.txt" after "cat", this indicates that we want to send the output characters into file "test2.txt".  This is called "redirection of standard output".  Thus, the command "cat > test2.txt" will create file "test2.txt" and allow us to type directly into that file.  It's an easy way to create a short text file. NOTE: To end your character input, press Control-D.  This is a special end-of-file character in Unix.  (Don't try this yet, but Control-D typed at the prompt will indicate the end of your command input and exit the shell.)  Enter the "cat > test2.txt" command, type "This is a test." and press Control-D.  There are many other ways to create and edit files. The simple console editor "nano" is standard in Linux distributions.  I prefer "emacs".  However, a gentle graphical editor program on our system is "gedit". 

If you are in need of a non-graphical editor for this portion of the tutorial (e.g. working remotely), you may alternatively use nano, a very simple text editor that should be available on most Unix/Linux installations. Here is a cheatsheet, and a video demonstration (esp. 3:45-5:29, 11:44-12:50) of remote programming with nano. The next step in the tutorial would then instead be "nano test3.txt". To quit nano, type Control-X. To suspend editing temporarily, type Control-Z. To resume suspended editing, enter the command "fg", short for "foreground", as Control-Z put the editor suspended into the background.

Create a file "test3.txt" with the content "This is another test." using the command "gedit test3.txt &".  Notice the ampersand (&) on the end.  This means "and I want to enter another command while this is running".  Without the "&", you will not get another command prompt until the "gedit" command completes, that is, until you exit gedit.  Generally speaking, if you want to execute a long-running command in the background or want to launch a graphical application you plan to use for a while, add "&" to the end of your command.   Once you've created all three text files, check the contents of your test3.txt with the command "cat test3.txt".  If this were a longer file that would fill many screens, it would be common to page through the file using the command "less test3.txt".  With "less", you can advance a page by pressing the spacebar, advance a line by pressing Enter, go back a  page by pressing "b", and quit by pressing "q".  Note that "less" leaves no printed output in your terminal window when you quit.

nuyuzr01@cs1:~/test/subdir1$ touch test.txt
nuyuzr01@cs1:~/test/subdir1$ ls
test.txt
nuyuzr01@cs1:~/test/subdir1$ cat > test2.txt
This is a test.
nuyuzr01@cs1:~/test/subdir1$ gedit test3.txt &
nuyuzr01@cs1:~/test/subdir1$ cat test3.txt
This is another test.
nuyuzr01@cs1:~/test/subdir1$ less test3.txt

File removal is accomplished with the command "rm" (remove).  Create a file "testrm.txt" with the command "touch", and then delete it with the "rm" command:

nuyuzr01@cs1:~/test/subdir1$ touch testrm.txt
nuyuzr01@cs1:~/test/subdir1$ rm testrm.txt

(There is also a "-r" option for the "rm" command that allows one to recursively remove a directory, its contents, all directories within it, etc.  Be very careful in your use of "rm -r" and don't use it at this point.)

Let's now get oriented and check our work.  List the current working directory, denoted ".", and then list the directory above, that is, containing this directory, denoted "..".  You should see three text files in the current working directory, and the two subdirectorys in the directory above, that is, the parent directory.

nuyuzr01@cs1:~/test/subdir1$ ls .
test2.txt  test3.txt  test.txt
nuyuzr01@cs1:~/test/subdir1$ ls ..
subdir1  subdir2

To move a file from one place to another, we use the "mv" (move) command followed by the file(s) that are to be moved and then finally the directory where they should be moved.  Let's move file "test2.txt" into the other subdirectory "subdir2".  Relative to our current working directory, we need only type the filename "test2.txt" to specify our file to move.  Relative to our current directory "subdir1", we want to move the file up one directory ("..") and then down into the destination directory "subdir2", so we can express this as "../subdir2".   Thus "mv test2.txt ../subdir2" will move file "test2.txt" into the "sibling" directory "subdir2" by relatively referring to it through the parent ("..").  To copy a file is similar and makes use of the command "cp" (copy).   As you can see, many common shell commands are short.  Combined with tab completion and other conveniences, you can get things done much faster than with a graphical interface. 

nuyuzr01@cs1:~/test/subdir1$ mv test2.txt ../subdir2
nuyuzr01@cs1:~/test/subdir1$ cp test3.txt ../subdir2

Did you use tab completion with the previous commands?  If not, please to practice it in the next steps.  Here, we repeat the copy command twice, but using different ways of referring to files.  As we saw before with the command "pwd", our working directory expressed from the root directory of the system (/) is rather complex.   The command "pwd" returns an absolute path.  A path name beginning with "~", ".", or ".." is a path relative to the home directory, current working directory, or directory above the current working directory, respectively.  In this current state, entering "ls ..", "ls ~/test", or  "ls /Accounts/turing/students/s20/nuyuzr01/test/" (substituting your absolute path, of course) will yield the same result because these list the contents of the same directory using different ways of expressing the path to the home directory.

Repeat "pwd" if you need to be reminded of your absolute path and then use tab completion to repeat the cp command using the "absolute" file/directory name starting with the root directory "/".  Then, practice using completion to refer to the file/directory name relative to your home directory (~). 

nuyuzr01@cs1:~/test/subdir1$ pwd
/Accounts/turing/students/s20/nuyuzr01/test/subdir1
nuyuzr01@cs1:~/test/subdir1$ cp /Accounts/turing/students/s20/nuyuzr01/test/subdir1/test3.txt /Accounts/turing/students/s20/nuyuzr01/test/subdir2
nuyuzr01@cs1:~/test/subdir1$ cp ~/test/subdir1/test3.txt ~/test/subdir2

It is often the case that one wishes to find a file/directory with a given name or pattern within a name, or a file that has a certain word or phrase in its contents.  First, change to your home directory with "cd".  Suppose you wish to find all filenames/directories that contain "sub" in their name.  Enter the command "find . -name *sub*".  This searches through the entire directory structure at or below directory ".", your working directory, and prints the relative file/directory names that have "sub" preceded and followed by 0 or more arbitrary characters, denoted by the "wildcard" character "*".   Suppose you wish to find all files that contain the word "test".  Enter the command 'grep -Ril "test" test', where option "R" calls for a recursive search through subdirectories, "i" specifies that search should ignore case, and "l" lists only the file where the pattern is found, rather than listing all occurrences within the file.  All possible uses of find and grep and the "regular expression" pattern language that includes wildcard "*" could be a lengthy tutorial in and of itself.  Remember to use the "man" command and internet searches to gradually increase your bash command line skills over time.

nuyuzr01@cs1:~/test/subdir1$ cd
nuyuzr01@cs1:~$ find . -name *sub*
./test/subdir2
./test/subdir1
nuyuzr01@cs1:~$ grep -Ril "test" test
test/subdir2/test3.txt
test/subdir2/test2.txt
test/subdir1/test3.txt

 
But wait! There's more!  Look what you also get ABSOLUTELY FREE!...

This tutorial gives just a brief orientation to bash, but there is much more that can be learned.  If you have an interest in Unix system administration (here or elsewhere), mastery of a shell such as bash is important and it is recommended that you go much deeper on your own.  Bash is the default shell for Linux, the default Terminal app shell for Macs, and can be emulated on Windows systems via free software such as Mobaxterm or Cygwin.  Combined with the latest Java development kit and GNU emacs for all platforms, you can have a very Unix-like Java development environment on whichever system you have for free.  Cooool.

If you're doing this tutorial as a class exercise, type the command "(pwd; history) | cat >> README" in your homework directory.  This will append a record of your work to a file called README which will be submitted with your assignment.  Then move this README file to the same directory you'll be submitting your homework from. WARNING: If you've already created a README file in that directory, moving this new README there will replace your other one.  In this case, replace "README" in the command above with the filename of your preexisting README file, e.g. "~/workspace/cs111/README". 

Now that you're well on your way to learning bash, the responsibility to learn more rests on your shoulders.  Remember that small time invested up front in this learning can save you much more time in the long run.  Also keep in mind that the computing habits you form now will stay with you for a career, so please take this independent learning seriously.  Not everything worthwhile should be assigned.  More in-depth Unix and bash tutorials and references can be found online:

 Enjoy your learning!

Todd W. Neller