Shells

What is a shell?

A [Li|U]nix shell is the command-line interface between the user and the system. It is used to perform some action, specifically, typing commands and displaying output, requested by the user.

Introduction to Bash

Bash is known as the Bourne-again shell, written by Brian Fox, and is a play on the name of the previously named Bourne shell (/bin/sh) written by Steve Bourne. [1]

Shell Fundamentals

Command-line Editing Modes

Over time, as one progresses with using the shell, it will become necessary to execute commands that may contain many arguments or have output piped to other commands. Beyond simple commands such as cat and ls, it can be tedious should one need to re-type, or, in most cases, edit, a given command. Luckily, bash provides a facility to make this scenario easier: the command-line editing mode.

The command-line editing mode emulates the movement functions of two common text editors, emacs and vi. In the case of the shell, the cursor’s movement is being controlled.

Todo

Tighten up the above sentence. It’s wordy and doesn’t seem to make the point I want it to make.

By default, bash operates in emacs mode.

Example Edit Commands

The following commands are very common while using the emacs mode.

  • Ctrl-b: Move backward by one character
  • Ctrl-f: Move forward by one character
  • Ctrl-a: Move to the beginning of the line
  • Ctrl-e: Move to the end of the line
  • Ctrl-k: Delete from the cursor forward
  • Ctrl-u: Delete from the cursor backward
  • Ctrl-r: Search the command history

The following commands are very common while using the vi mode.

  • h: Move backward by one character
  • l: Move forward by one character
  • w: Move forward by one word
  • 0: Move to the beginning of the line
  • $: Move to the end of the line
  • d$: Delete from the cursor to the end of the line
  • d0: Delete from the cursor the beginning of the line
  • :history s: Search the command history

Setting the Mode

One can manually set the desired mode using the below commands.

  • emacs

    set -o emacs

  • vi

    set -o vi

See Text Editing 101 for details on appropriate edit commands to use on the command line.

Environment variables

Environment variables are used to define values for often-used attributes of a user’s shell. In total, these variables define the user’s environment. Some environment variables provide a simple value describing some basic attribute, such the user’s current directory ($PWD). Others define the behavior of a command, such as whether or not the history command should log repeated commands individually or log the repeated command once ($HISTCONTROL).

$PATH

The most common, or most recognized, environment variable is the $PATH variable. It defines the set of directories that the shell can search to find a command. Without an explicit path provided when calling a command (i.e. /bin/ps), the shell will search the directories listed in the $PATH variable until it finds the command. If the command is not found in any of the defined directories in $PATH, the shell will produce an error explaining as much.

$ foobar -V
-bash: foobar: command not found

To view the contents of the $PATH variable, use echo to print the variable’s value:

$ echo $PATH
/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin

The order of the directories in the $PATH variable, from left to right, is important; when searching directories for a command, the shell will stop looking after it finds its first match. In other words, using our example $PATH variable above, if there is a version of ps that exists in /usr/local/bin that is preferred (by the sysadmin) over the version that exists in /bin, the shell will still execute /bin/ps due to the precedence of the directories defined in the $PATH variable.

To list all of the shell’s environment variables, use the env command:

$ env
HOSTNAME=foobar
SHELL=/bin/bash
TERM=xterm
HISTSIZE=1000
USER=root
PATH=/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/usr/local/bin
MAIL=/var/spool/mail/root
PWD=/root/curriculum
PS1=[\[\e[33;1m\]\t \[\e[31;1m\]\u\[\e[0m\]@\[\e[31;1m\]\h\[\e[0m\] \W\[\e[0m\]]#
HISTCONTROL=ignoredups
SHLVL=1
SUDO_COMMAND=/bin/bash
HOME=/root
HISTTIMEFORMAT=[%Y-%m-%d %H:%M:%S]
OLDPWD=/tmp

Shell Profiles

Shell profiles are used to define a user’s environment. In cases where an environment variable needs to be set or a script needs to be run at login, a profile can be used to ensure this happens consistently and automatically. Anything that can be run in a standalone shell script can be placed in a profile.

There are two types of profiles:

  • Global profile (/etc/profile)
  • User profile (~/.bash_profile)

/etc/profile

This is the global profile. Any environment variable set in this file applies to all users. Any script called from this file is executed for all users.

~/.bash_profile

This is the user profile. Any environment variable set in this file applies to the user only. Any script called from this file is executed for the user only.

Profile Precedence

NOTE: It is possible to override settings from /etc/profile via ~/.bash_profile as ~/.bash_profile is executed after /etc/profile.

Special Environment Variables

Certain variables exist that provide useful information about what is happening in the environment. For example, it may be necessary to know the ID of a running process or the exit status of an executed command.

Process ID: $$

To determine the process ID (PID) of the current shell, first run ps to find the PID, then run echo $$ to confirm the PID.

$ ps -efl | grep bash
 502 20440 20439   0 10:25PM ttys001    0:00.01 -bash                4006  31  0  2433436   1228 -      S                   0
 502 20447 20440   0 10:29PM ttys001    0:00.00 grep bash            4006  31  0  2432768    620 -      R+                  0
$ echo $$
20440

Background Process ID: $!

Occasionally, commands will need to be executed in the background (via the & operator). The PID of that process can be found using the $! variable. For example, call sleep for 30 seconds and place it in the background. Then echo $! to see the PID. Alternatively, call ps to confirm the PID.

$ sleep 30 &
[1] 20450
$ echo $!
20450
$ ps -efl | grep sleep
502 20450 20440   0 10:33PM ttys001    0:00.00 sleep 30             4006  31  0  2432748    496 -      S                   0

Exit Status: $?

When a command is executed, it always has an exit status. That status defines whether or not the command was successful. For success, the exit status is 0. Non-zero values denote failure. Many commands provide multiple exit codes to help define what the reason for failure was. This helps the user troubleshoot any problems.

As an example, try to list a known file within a user’s home directory, then list a file that is known not to exist. After each command, execute echo $? to see the exit status.

$ ls .bash_profile
.bash_profile
$ echo $?
0
$ ls foobar
ls: foobar: No such file or directory
$ echo $?
1

History

The history is a handy facility within bash; it stores all of the commands that the user has executed.

To see the history, simply type history and all of the stored commands will be displayed to the terminal. Similarily, run cat ~/.bash_history to see all stored commands.

Re-executing Commands

A benefit of storing command history is that the commands can be easily recalled. To execute the last command, type !!:

$ ls
file1 file2 dir1
$ !!
ls
file1 file2 dir1

Note that the last command is echoed just above the output of that command.

To execute the nth command in history, run !n where n is the line number of the command as found in the output of history:

$ history | less
  1  ls -la
  2  ls -F
  3  pwd
$ !2
ls -F
file1 file2 dir1/
Searching History

Finally, one can search the history by typing Ctrl-r followed by a string to match a command:

$ (reverse-i-search)`ls': ls -F

To execute the command (if a match is found), simply type Enter.

Job control

From time to time, it may be necessary to manage commands running in the background. These are typically referred to as jobs. A command can be placed in the background via the & operator. Use the jobs command to see the job in the background. To bring it back to the foreground, run fg:

[23:24:22 ~]$ sleep 30 &
[1] 20969
[23:24:26 ~]$ jobs
[1]+  Running                 sleep 30 &
[23:24:29 ~]$ fg
sleep 30
[23:24:56 ~]$

While in the foreground, the job can be suspended via Ctrl-z and sent to the background once more using bg:

[23:24:56  ~]$ sleep 30 &
[1] 21078
[23:28:25  ~]$ jobs
[1]+  Running                 sleep 30 &
[23:28:27  ~]$ fg
sleep 30
^Z
[1]+  Stopped                 sleep 30
[23:28:33  ~]$ bg
[1]+ sleep 30 &
[23:28:37  ~]$ jobs
[1]+  Running                 sleep 30 &
[23:29:39 ~ ]$ jobs
[1]+  Done                    sleep 30

Multiple Jobs

It is possible to have multiple jobs running in the background at the same time. Use the jobs command to track them via their job ID, noted between the square brackets after sending a job to the background. Knowing the job ID is helpful in the event that the job needs to be pulled into the foreground (via fg) or if the job needs to be killed:

[23:36:01  ~]$ sleep 120 &
[1] 21086
[23:36:16  ~]$ sleep 240 &
[2] 21087
[23:36:20  ~]$ jobs
[1]-  Running                 sleep 120 &
[2]+  Running                 sleep 240 &
[23:36:21  ~]$ fg %1
sleep 120
^C
[23:36:33  ~]$ jobs
[2]+  Running                 sleep 240 &
[23:36:35  ~]$ kill %2
[23:36:39  ~]$ jobs
[2]+  Terminated: 15          sleep 240

NOTE: When manipulating jobs with any command, the jobs are described by their ID using the %n notation where n is the job ID.

For information on ensuring running jobs continue, even when terminal connectivity is lost, see the sections on GNU Screen and Tmux.

Footnotes

[1]C Programming by Al Stevens, Dr. Dobb’s Journal, July 1, 2001
Read the Docs v: latest
Versions
latest
Downloads
pdf
htmlzip
epub
On Read the Docs
Project Home
Builds

Free document hosting provided by Read the Docs.