Configuring and Using bash

As mentioned earlier, bash is Mac OS X’s default Unix shell. Apple made the switch from tcsh to bash because of its support for Unicode text, something that’s very important in the international market. Another logical reason for switching to bash is that it is the default shell for most Linux distributions and is easier to script with than tcsh. Because bash is now the default shell, this book focuses on its use, with an occasional nod to the other shells where appropriate.

The default configuration of bash is perfectly adequate for casual usage, but you’ll inevitably want to configure it to your own liking. This section takes a look at the various configuration files for bash, its environment variables , how to set up command aliases, and how to use bash’s history to your advantage. You’ll also learn a bit about redirecting output between commands and into files, as well as some basic shell loops.

Environment Variables

Every program on the system runs in an environment. The environment consists of a set of name-value pairs, known as environment variables, which communicate a variety of configuration settings to a program. For example, the shell uses the PATH environment variable to find a program to execute in response to a command. To get an idea of what kinds of data are stored in environment variables, execute the set command, as shown in Example 4-5.

Example 4-5. Examining environment variables with set

$ set
BASH=/bin/bash
BASH_VERSINFO=([0]="2" [1]="05b" [2]="0" [3]="1" [4]="release" 
[5]="powerpc-apple-darwin8.0")
BASH_VERSION='2.05b.0(1)-release'
COLUMNS=80
DIRSTACK=()
EUID=501
GROUPS=()
HISTFILE=/Users/jldera/.bash_history
HISTFILESIZE=500
HISTSIZE=500
HOME=/Users/jldera
HOSTNAME=ronin.local
HOSTTYPE=powerpc
...

When you execute the set command, you’ll see quite a bit of output—probably around 40 lines. Some of the environment variables may make sense when you first look at them, and some won’t. Table 4-6 lists some of the environment variables you are likely to use on occasion.

Table 4-6. Commonly used bash environment variables

Variable

Description

BASH

Location of the bash shell program

BASH_VERSION

Version of bash currently running

COLUMNS

Number of columns to use in Terminal view

DIRSTACK

List of directories used by the pushd and popd commands

GROUPS

Various groups with which the user is associated

HISTFILE

File containing the shell history

HOME

Home directory for the user

HOSTNAME

Name of the system on which the shell is running

LINES

Number of lines currently being used by the shell

PATH

List of directories the shell uses to resolve commands

PS1

String used as the primary prompt

PS2

String used as the secondary prompt

SHELL

Shell program being used

SHELLOPTS

Options in effect for the shell

TERM

Type of terminal that the shell is displaying its content to

UID

User ID of the currently logged-in user

USER

Username of the currently logged-in user

_ (underscore)

Previously executed command

To see the value of a single environment variable such as PATH, you can use the echo command, as shown in Example 4-6.

Example 4-6. Using the echo command to examine an environment variable

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

The dollar sign in front of PATH means you are referring to an environment variable. If you had just entered echo PATH, the shell would return PATH, as shown in Example 4-7.

Example 4-7. Results of using the echo command without a dollar sign

$ echo PATH
PATH

To set or change an environment variable for the lifetime of the shell, use the export command. For example, to change the PATH variable so that you can run your own commands in ~/bin, you could use the following command:

$ export PATH=$PATH:~/bin

This sets the PATH variable to the currently existing PATH with the ~/bin directory appended to it. An important thing to remember here is that you need to use a colon (:) as a delimiter between command paths . These commands will last for the lifetime of the current shell, which is as long as that Terminal window is open. To add these paths to the shell permanently, you’ll have to edit one of bash’s configuration files , described next.

Configuration Files

When bash first starts, it looks for run command files. Commonly called rc files, Unix apps use these files to store basic configuration data that is used as the program loads. As a matter of fact, there are some special rc files in the /etc folder that are involved with the initial setup and loading of Mac OS X itself. You can learn some more about them in Chapter 5.

The bash shell first looks at the /etc/profile file for its initial state. This is a system-wide set of default settings that are superseded by any other files subsequently loaded by bash. Next, three files in the Home directory, if they exist, are used to configure bash:

.bash_profile

Contains environment variables and commands that are read and executed every time you create a new Terminal window and a shell is created for it, or when you SSH into your machine and are presented with a prompt. This allows you to customize the shell to your liking. If bash doesn’t find this file, it looks for .bash_login and .profile respectively to fill in for it.

.bashrc

Contains environment variables and commands that are read and executed only when you create a subshell by typing bash in an already running shell.

.bash_logout

Contains commands that are read and executed when you log out of a shell. You could use this to clean up files before you log out.

By default, these files don’t exist as part of a user’s Home directory until you create them. The most useful of these three files is .bash_profile, which is used to customize the shell. For example, if you wanted to permanently modify the PATH that the shell uses to resolve commands, you could create a .bash_profile file in your Home directory and add the following line:

PATH=$PATH:~/bin:/Developer/Tools

This causes the PATH environment variable to be set to the given string each time you open a new Terminal window. Because .bash_profile is only read when the shell is created, any changes you make to it won’t take effect until you start the next shell. If you don’t want to close your shell and start a new one, you can use the source command to load the contents of the .bash_profile file:

$ source ~/.bash_profile

Aliases

In addition to searching the PATH for commands, the bash shell lets you define a set of aliases . Aliases are commonly used to create a shorter command name for long command strings so that they’re a bit more manageable or to rename commonly used commands. It’s important to note that these are not the same aliases as those defined in the Finder. Finder aliases are closer to the Unix concept of a symbolic link. It’s a shortcut to a file instead of a shortcut for a command.

To define an alias for a command, use the following syntax:

alias name=command

Where name is the name of the command alias you are defining, and command is the command that’s actually executed by the shell when you invoke the alias. One common use of aliases is to accommodate fat-fingering of commands. For example, if you are always typing sl instead of ls, you could define the following alias so that you don’t get scolded by the shell again:

$ alias sl=ls

Another use for aliases is to create a simple command for a longer one. For example, if you are often changing directories to somewhere deep in the hierarchy, you can set up an alias that will allow you to go there quickly:

$ alias fdocs="cd ~/Documents/Corporate/Master/Forecasts"

Notice the use of quotes around the command. This is required when a command consists of more than one word.

Yet another use for aliases is to redefine a command to add some default options. For example, if you’re constantly forgetting about the hidden dot files on your machine, you can redefine the way the shell handles the ls command:

$ alias ls="ls -a"

As another example, if you’re still getting used to the fact that the shell doesn’t make use of the Trash can, you can make the rm command ask you to confirm file deletions by using its -i switch:

$ alias rm="rm -i"

To get a list of all the aliases currently defined, use the alias command by itself, as shown in Example 4-8.

Example 4-8. Examining the currently defined shell aliases

$alias
alias sl=ls
alias fdocs="cd ~/Documents/Corporate/Master/Forecasts"

You can even make aliases to GUI applications. For example, if you wanted to create a quick shortcut to open the Safari browser while on the command line, you could define the following alias:

$ alias safari="open -a Safari"

With this alias in place, to launch Safari, you simply need to type safari into the command line. And remember, to make an alias permanent, you’ll need to create a .bash_profile file and place the alias into it or edit your existing .bash_profile file and then source it so the change takes effect.

History

As bash runs, it keeps a history of the commands that you’ve executed. This feature is quite handy as it lets you look at and reuse commands that you’ve previously entered. Where the shell’s history is particularly useful is when you need to invoke a command that has a lengthy set of parameters that you can’t remember.

The simplest way to use the history is to use the up and down arrows on your keyboard. This will step back and forth through the commands that you’ve executed and display each in turn at the prompt. To get more out of the history, you can use the history command, which displays a list of previously executed commands. Example 4-9 shows some output from history.

Example 4-9. Using the history command

$ history
1 cd Documents
2 ls
3 open -a Safari
4 history

The commands are listed in the order in which they were executed. To reuse a particular command, type ! (exclamation point; also called “bang” by Unix geeks), followed by the number of the command you want to reuse. Example 4-10 shows how to use this command.

Example 4-10. Using a command from the history

$!2
ls
Adobe SVG 3.0
Installer Log
Music
Desktop
Pictures
Documents
Public
Library
Sites
Movies
Work

Note that the shell tells you which command is running as it runs the command. This is useful because you can tell what arguments are being used. Another way to navigate the history list is to use the first few letters of the command instead of the command number. Example 4-11 shows how to quickly execute the last command that started with an o character.

Example 4-11. Executing a command from the history based on character

$ !o open -a Safari

Whenever you exit bash, it writes its history to the ~/.bash_history file. Likewise, whenever you start bash, it populates its history with the contents of the ~/.bash_history file. This allows you to quit and restart your shell and still have your history available to you. By default, the history file is set to retain up to the last 500 commands. To change this value, set the HISTFILESIZE environment variable to the number of lines that you want to keep. For example, to change it to remember 1,000 commands instead of 500, you would use the following:

$ export HISTFILESIZE=1000

Once again, if you’d like to make this change permanent, you’ll have to add it to your ~/.bash_profile file.

Redirecting Output

If you’ve been using the shell for a while, the history command might have so many entries to display that they scroll by too quickly for you to read them. Earlier in the chapter, you learned about the more command. It might have occurred to you that this would be a perfect opportunity to use more to paginate the history command’s output. However, it may not have been immediately obvious how to do so.

This is where one of the most powerful features of the Unix shell comes into play. The shell allows you to redirect the output from one command and pass it along to another command. It also enables you to redirect a command’s output to a file for later perusal. For example, if the history command’s output is scrolling too quickly for you, you could redirect its output to the more command as follows:

$ history | more

The pipe (|) character is used for passing the data between commands. For saving the output to a file, you’d use the greater than character (>). So if you wanted to save a directory listing of your /Applications folder to a file on your Desktop, use the command:

$ ls -l /Applications > ~/Desktop/DirectoryListing.txt

You can even append output from a command to the end of a file by using two greater than symbols (>>). If you want to add the directory listing of your /Applications/Utilities folder to the file on your desktop:

$ ls -l /Applications/Utilities >> ~/Desktop/DirectoryListing.txt

For some fun with redirecting data between commands, try using the ls command to send a directory listing to the say command. Just make sure your speakers are on to hear the results.

Loops

Another powerful feature the shell gives you is the ability to loop through commands. This will come into play more in Chapter 13 when shell scripts are covered, but for now, here’s a basic example of a loop. Let’s say that you have several text files on your Desktop that you want the say command to read aloud to you. After reading the discussion of wildcards earlier in the chapter, you might think the following command would work:

$ say ~/Desktop/*.txt

However, executing that command will make say merely speak the files’ names, not read the files’ contents to you. To have each file read aloud, you need to supply each file’s name to the say command, using only one file each time you issue the command. This can quickly become tedious if you have several files you want to have read aloud.

If you enter the shell commands shown in Example 4-12, you can make the shell do all of that work for you:

Example 4-12. A simple shell loop

$ for i in $(ls ~/Desktop/*.txt)
> do
> say -f $i
> done

The first line of this loop starts with the for command, which tells bash that we’re defining a loop. The next value, i, is a temporary variable to hold a single file’s name. The in portion tells for that the part that follows, $(ls ~/Desktop/*.txt), is where it should look for the values to place in i. The $() convention is used to have the shell place the output of one command (in this case, ls ~/Desktop/*.txt) into another, but isn’t quite the same as redirecting the output using a pipe.

The do line indicates to the shell that the commands that follow are the contents of the loop. The loop is closed with a simple done command. When executed, the shell will use the ls command to find all files in ~/Desktop that end in .txt. The shell will then take the first result, store it in the variable i, and process the commands between do and done. Once the commands have been executed, the shell takes the next value from the directory listing results, places it in the i variable, and then loops through the commands again.

Get Running Mac OS X Tiger now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.