Chapter 7. The shell

Table of Contents

7.1. Introduction
7.2. Executing commands
7.3. Moving around
7.4. Command history
7.5. Completion
7.6. Wildcards
7.7. Redirections and pipes

7.1. Introduction

In this chapter we will look at the traditional working environment of UNIX systems: the shell. The shell is an interpreter that can be used interactively and non-interactively. When the shell is used non-interactively it functions as a simple, but powerful scripting language.

The procedure for starting the shell depends on whether you use a graphical or text-mode login. If you are logging on in text-mode the shell is immediately started after entering the (correct) password. If you use a graphical login manager like KDM, log on as you would normally, and look in your window manager or desktop environment menu for an entry named “XTerm”, “Terminal” or “Konsole”. XTerm is a terminal emulator, after the terminal emulator is started the shell comes up.

Before we go any further, we have to warn you that Slackware Linux provides more than just one shell. There are two shell flavors that have become popular over time, the Bourne shell and the C shell. In this chapter we will describe Bourne shells that conform to the IEEE 1003.1 standard. The Bash (Bourne Again Shell) and ksh (Korn Shell) shells conform well to these standards. So, it is a good idea to use one of these two shells. You can easily see what shell the system is running by executing echo $SHELL. This is what a Bash shell may report:

$ echo $SHELL
/bin/bash
    

If you are using a different shell, you can change your default shell. Before setting a different shell, you have to establish the full path of the shell. You can do this with the which command. For example:

$ which bash
/bin/bash
$ which ksh
/bin/ksh
    

On this Slackware system, the full path to the bash shell is /bin/bash, and to the ksh shell /bin/ksh. With this information, and the chsh command you change the default shell. The following example will set the default shell to bash:

$ chsh -s /bin/bash
Changing shell for daniel.
Password:
Shell changed.
    

The new shell will be activated after logging out from the current shell (with logout or exit), or by opening a new X terminal window if you are running X11.

7.2. Executing commands

An interactive shell is used to start programs by executing commands. There are two kinds of commands that a shell can start:

  • Built-in commands: built-in commands are integrated in the shell. Commonly used built-in commands are: cd, fg, bg, and jobs.

  • External commands: external commands are programs that are not part of the shell program, and are separately stored on the filesystem. Commonly used external commands are: ls, cat, rm, and mkdir.

All shell commands are executed with the same syntax:

commandname [argument1 argument2 ... argumentn]
    

The number of arguments is arbitrary, and are always passed to the command. The command can decide what it does with these arguments.

All built-in commands can always be executed, because they are part of the shell. External commands can be executed by name when the program is in the search path of the shell. Otherwise, you will have to specify the path to the program. The search path of the shell is stored in a variable named PATH. A variable is a named piece of memory, of which the contents can be changed. We can see the contents of the PATH variable in the following manner:

$ echo $PATH
/usr/kerberos/bin:/usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin:/home/daniel/bin
    

The directory paths in the PATH variable are separated with the colon (:) character. You can use the which command to check whether a given command is in the current shell path. You can do this by providing the command as an argument to which. For example:

$ which pwd
/bin/pwd
$ which sysstat
/usr/bin/which: no sysstat in (/usr/kerberos/bin:/usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin:/home/daniel/bin)
    

If a program is not in the path, you can still run it by entering its absolute or relative path.

7.3. Moving around

It is often necessary to jump through various parts of a line, and to alter it, when you are editing larger commands. Both bash and ksh have keyboard shortcuts for doing common operations. There are two shell modes, in which the shortcut keys differ. These modes correspond with two popular editors for UNIX in their behavior. These editors are vi and emacs. In this book we will only cover the EMACS-like keystrokes. You can check in which mode a shell is running by printing the SHELLOPTS variable. In the first example the shell is used in emacs mode, in the second example the vi mode is used. You identify the mode by looking for the emacs or vi strings in the contents of the variable.

$ echo $SHELLOPTS
braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor
    
$ echo $SHELLOPTS
braceexpand:hashall:histexpand:history:interactive-comments:monitor:vi
    

If your shell is currently using the vi mode, you can switch to the emacs mode by setting the emacs option:

$ set -o emacs
    

With the emacs editing mode enabled, you can start using shortcuts. We will look at three kinds of shortcuts: character editing shortcuts, word editing shortcuts, and line editing shortcuts. Later in this chapter, we will also have a look at some shortcuts that are used to retrieve entries from the command history.

7.3.1. Character editing

The first group of shortcuts have characters as their logic unit, meaning that they allow command line editing operations on characters. Table 7.1, “Moving by character” provides an overview of the shortcuts that are used to move through a line by character.

Table 7.1. Moving by character

Keys Description
Ctrl-b Move a character backwards.
Ctrl-f Move a character forward.

These shortcuts are simple, and don't do anything unexpected. Suppose that you have typed the following line:

find ~/music -name '*.ogg' - -print 
      

The cursor will be at the end. You can now move to the start of the line by holding Ctrl-b:

find ~/music - -name '*.ogg' -print
      

Likewise, you can go back again to the end by holding Ctrl-f. There is an error in this line, since there is one erroneous dash. To remove this dash, you can use one of the character deletion shortcuts.

Table 7.2. Deleting characters

Keys Description
Ctrl-h Delete a character before the cursor. This has the same effect as using the Backspace key on most personal computers.
Ctrl-d Delete the character the cursor is on.

You can delete the dash in two manners. The first way is to move the cursor to the dash:

find ~/music - -name '*.ogg' -print
      

and then press Ctrl-d twice. This will delete the dash character, and the space that follows the dash:

find ~/music -name '*.ogg' -print
      

Looking at the original fragment, the other approach is to position the cursor on the space after the dash:

find ~/music -  -name '*.ogg' -print
      

and then press Ctrl-h twice to delete the two preceding characters, namely the dash and the space before the dash. The result will be the same, except that the cursor will move:

find ~/music -name '*.ogg' -print
      

One of the nice features of most modern shells is that you can transpose (swap) characters. This is handy if you make a typing error in which two characters are swapped. Table 7.3, “Swapping characters” lists the shortcut for transposing characters.

Table 7.3. Swapping characters

Keys Description
Ctrl-t Swap (transpose) the characters the cursor is on, and the character before the cursor. This is handy for quickly correcting typing errors.

Suppose that you have typed the following command:

cat myreport.ttx
      

The extension contains a typing error if you intended to cat myreport.txt. This can be corrected with the character transpose shortcut. First move to the second character of the pair of characters that are in the wrong order:

cat myreport.ttx
      

You can then press Ctrl-t. The characters will be swapped, and the cursor will be put behind the swapped characters:

cat myreport.txt 
      

7.3.2. Word editing

It if often tedious to move at character level. Fortunately the Korn and Bash shells can also move through lines at a word level. Words are sequences of characters that are separated by a special character, such as a space. Table 7.4, “Moving by word” summarizes the shortcuts that can be used to navigate through a line by word.

Table 7.4. Moving by word

Keys Description
Esc b Move back to the start of the current or previous word.
Esc f Move forward to the last character of the current or next word.

As you can see the letters in these shortcuts are equal to those of moving forward and backwards by character. The movement logic is a bit curious. Moving forward puts the cursor to the end of the current word, not to the first character of the next word as you may have predicted. Let's look at a quick example. In the beginning the cursor is on the first character of the line.

find ~/music -name '*.ogg' -print
      

Pressing Esc f will move the cursor behind the last character of the first word, which is find in this case:

find ~/music -name '*.ogg' -print
      

Going forward once more will put the cursor behind ~/music:

find ~/music -name '*.ogg' -print
      

Backwards movement puts the cursor on the first character of the current word, or on the first character of the previous word if the cursor is currently on the first character of a word. So, moving back one word in the previous example will put the cursor on the first letter of “music”:

find ~/music -name '*.ogg' -print
      

Deleting words works equal to moving by word, but the characters that are encountered are deleted. Table 7.5, “Deleting words” lists the shortcuts that are used to delete words.

Table 7.5. Deleting words

Keys Description
Alt-d Delete the word, starting at the current cursor position.
Alt-Backspace Delete every character from the current cursor position to the first character of a word that is encountered.

Finally, there are some shortcuts that are useful to manipulate words. These shortcuts are listed in Table 7.6, “Modifying words”.

Table 7.6. Modifying words

Keys Description
Alt-t Swap (transpose) the current word with the previous word.
Alt-u Make the word uppercase, starting at the current cursor position.
Alt-l Make the word lowercase, starting at the current cursor position.
Alt-c Capitalize the current word character or the next word character that is encountered.

Transposition swaps words. If normal words are used, it's behavior is predictable. For instance, if we have the following line with the cursor on “two

one two three
      

Word transposition will swap “two” and “one”:

two one three
      

But if there are any non-word characters, the shell will swap the word with the previous word while preserving the order of non-word characters. This is very handy for editing arguments to commands. Suppose that you made an error, and mixed up the file extension you want to look for, and the print parameter:

find ~/music -name '*.print' -ogg
      

You can fix this by putting the cursor on the second faulty word, in this case “ogg”, and transposing the two words. This will give the result that we want:

find ~/music -name '*.ogg' -print
      

Finally, there are some shortcuts that change the capitalization of words. The Alt-u shortcut makes all characters uppercase, starting at the current cursor position till the end of the word. So, if we have the lowercase name “alice”, uppercasing the name with the cursor on “i” gives “alICE”. Alt-l has the same behavior, but changes letters to lowercase. So, using Alt-l on “alICE” with the cursor on “I” will change the string to “alice”. Alt-c changes just the character the cursor is on, or the next word character that is encountered, to uppercase. For instance, pressing Alt-c with the cursor on “a” in “alice” will yield “Alice”.

7.3.3. Line editing

The highest level we can edit is the line itself. Table 7.7, “Moving through lines” lists the two movement shortcuts.

Table 7.7. Moving through lines

Keys Description
Ctrl-a Move to the beginning of the current line.
Ctrl-e Move to the end of the current line.

Suppose that the cursor is somewhere halfway a line:

find ~/music -name '*.ogg' -print
      

Pressing Ctrl-e once will move the cursor to the end of the line:

find ~/music -name '*.ogg' -print 
      

Pressing Ctrl-a will move the cursor to the beginning of the line:

find ~/music -name '*.ogg' -print
      

You can also delete characters by line level. The shortcuts are listed in Table 7.8, “Deleting lines”. These shortcuts work like movement, but deletes all characters that are encountered. Ctrl-k will delete the character the cursor is on, but Ctrl-x Backspace will not. Moving to the beginning of the line with Ctrl-a, followed by Ctrl-k, is a fast trick to remove a line completely.

Table 7.8. Deleting lines

Keys Description
Ctrl-k Delete all characters in the line, starting at the cursor position.
Ctrl-x Backspace Delete all characters in the line up till the current cursor position.

7.4. Command history

It often happens that you have to execute commands that you executed earlier. Fortunately, you do not have to type them all over again. You can browse through the history of executed commands with the up and down arrows. Besides that it is also possible to search for a command. Press Control-r and start typing the command you want to execute. You will notice that bash will display the first match it can find. If this is not the match you were looking for you can continue typing the command (until it is unique and a match appears), or press Control-r once more to get the next match. When you have found the command you were looking for, you can execute it by pressing <Enter>.

7.5. Completion

Completion is one of the most useful functionalities of UNIX-like shells. Suppose that you have a directory with two files named websites and recipe. And suppose you want to cat the file websites (cat shows the contents of a file), by specifying websites as a parameter to cat. Normally you would type “cat websites”, and execute the command. Try typing “cat w”, and hit the <Tab> key. Bash will automatically expand what you typed to “cat websites”.

But what happens if you have files that start with the same letter? Suppose that you have the recipe1.txt and recipe2.txt files. Type “cat r” and hit <Tab>, Bash will complete the filename as far as it can. It would leave you with “cat recipe”. Try hitting <Tab> again, and Bash will show you a list of filenames that start with “recipe”, in this case both recipe files. At this point you have to help Bash by typing the next character of the file you need. Suppose you want to cat recipe2, you can push the <2> key. After that there are no problems completing the filename, and hitting <Tab> completes the command to “cat recipe2.txt”.

It is worth noting that completion also works with commands. Most GNU/Linux commands are quite short, so it will not be of much use most of the time.

It is a good idea to practice a bit with completion, it can save alot of keystrokes if you can handle completion well. You can make some empty files to practice with using the touch command. For example, to make a file named recipe3.txt, execute touch recipe3.txt.

7.6. Wildcards

Most shells, including Bash and ksh, support wildcards. Wildcards are special characters that can be used to do pattern matching. The table listed below displays some commonly used wildcards. We are going to look at several examples to give a general idea how wildcards work.

Table 7.9. Bash wildcards

Wildcard Matches
* A string of characters
? A single character
[] A character in an array of characters

7.6.1. Matching a string of characters

As you can see in the table above the “*” character matches a string of characters. For example, *.html matches everything ending with .html, d*.html matches everything starting with a d and ending with .html.

Suppose that you would like to list all files in the current directory with the .html extension, the following command will do the job:

$ ls *.html
book.html        installation.html     pkgmgmt.html  usermgmt.html
filesystem.html  internet.html         printer.html  xfree86.html
gfdl.html        introduction.html     proc.html
help.html        slackware-basics.html shell.html
      

Likewise we could remove all files starting with an in:

$ rm in*
      

7.6.2. Matching single characters

The “?” wildcard works as the “*” wildcard, but matches single characters. Suppose that we have three files, file1.txt, file2.txt and file3.txt. The string file?.txt matches all three of these files, but it does not match file10.txt (“10” are two characters).

7.6.3. Matching characters from a set

The “[]” wildcard matches every character between the brackets. Suppose we have the files from the previous example, file1.txt, file2.txt and file3.txt. The string file[23].txt matches file2.txt and file3.txt, but not file1.txt.

7.7. Redirections and pipes

One of the main features of UNIX-like shells are redirections and pipes. Before we start to look at both techniques we have to look how most UNIX-like commands work. When a command is not getting data from a file, it will open a special pseudo-file named stdin, and wait for data to appear on it. The same principle can be applied for command output, when there is no explicit reason for saving output to a file, the pseudo-file stdout will be opened for output of data. This principle is shown schematically in Figure 7.1, “Standard input and output”

Figure 7.1. Standard input and output

Standard input and output

You can see stdin and stdout in action with the cat command. If cat is started without any parameters it will just wait for input on stdin and output the same data on stdout. If no redirection is used keyboard input will be used for stdin, and stdout output will be printed to the terminal:

$ cat
Hello world!
Hello world!
    

As you can see cat will print data to stdout after inputting data to stdin using the keyboard.

7.7.1. Redirection

The shell allows you to take use of stdin and stdout using the “<” and “>”. Data is redirected in which way the sharp bracket points. In the following example we will redirect the md5 summaries calculated for a set of files to a file named md5sums:

$ md5sum * > md5sums
$ cat md5sums 
6be249ef5cacb10014740f61793734a8  test1
220d2cc4d5d5fed2aa52f0f48da38ebe  test2
631172a1cfca3c7cf9e8d0a16e6e8cfe  test3
      

As we can see in the cat output the output of the md5sum * output was redirected to the md5sums file. We can also use redirection to provide input to a command:

$ md5sum < test1
6be249ef5cacb10014740f61793734a8  -
      

This feeds the contents of the test1 to md5sum.

7.7.2. Pipes

You can also connect the input and output of commands using so-called pipes. A pipe between commands can be made with the “|” character. Two or more combined commands are called a pipeline. Figure 7.2, “A pipeline” shows a schematic overview of a pipeline consisting of two commands.

Figure 7.2. A pipeline

A pipeline

The “syntax” of a pipeline is: command1 | command2 ... | commandn. If you know how the most basic UNIX-like commands work you can now let these commands work together. Let's look at a quick example:

$ cat /usr/share/dict/american-english | grep "aba" | wc -l
123
      

The first command, cat, reads the dictionary file /usr/share/dict/american-english. The output of the cat command is piped to grep, which prints out all files containing the phrase “aba”. In turn, the output of “grep” is piped to wc -l, which counts the number of lines it receives from stdin. Finally, when the stream is finished wc prints the number of lines it counted. So, combined three commands to count the number of words containing the phrase “aba” in this particular dictionary.

There are hundreds of small utilities that handle specific tasks. As you can imagine, together these commands provide a very powerful toolbox by making combinations using pipes.