MacDevCenter    
 Published on MacDevCenter (http://www.macdevcenter.com/)
 See this if you're having trouble printing code examples


bash on Mac OS X

by David Miller
02/24/2004

If you're curious about this article, chances are you've played around with the Terminal in OS X and have become accustomed to the command-line interface, or you've worked with files through commands that you type rather than menus and icons that you click. If that's not the case, then you should first read Chris Stone's series of articles, titled "Learning the Mac OS X Terminal", and "Learning the Terminal in Jaguar."

In the switch from Jaguar to Panther, one of the footnotes that has occasionally been mentioned is the switch from tcsh to bash as the default shell in Mac OS X. Those who upgraded from Jaguar to Panther will still be using tcsh when they log in to Panther for the first time since your existing preferences will be respected; only new accounts that are created from within Panther will use bash by default. Most casual Terminal users won't notice the difference between the two, as the usual suspects will still work exactly as they did before:

But those who have played around with aliases, environment variables, or other parts of tcsh will find that their lovingly crafted environment is nonexistent when using bash. This article will help those who have played around with tcsh in Jaguar make the transition to bash in Panther (and, in doing so, will quite possibly contain more screen captures of the Terminal than any Mac DevCenter article before it).

Unix 101: What Is a Shell?

Related Reading

Learning the bash Shell
By Cameron Newham, Bill Rosenblatt

If you've reached this point, chances are that you know that the shell acts as the glue between you and the big, hairy beast that hides in the deepest, darkest recesses of your computer. This beast, known as the kernel, happens to be at the top of your computer's food chain; it governs the resources (memory, processor, and disk) that each process receives, and ensures that everything is running as it should.

So you can imagine what would happen if a kernel doesn't behave properly: applications start crashing, the dreaded pinwheel appears, and the computer generally starts doing weird things. Chances are you've stumbled across this situation before, even if you didn't know it at the time: Windows users are familiar with the "blue screen of death," while those of us who have used one of the various incarnations of Mac OS X have probably come across a few kernel panics here and there.

What you may not have known is that you aren't restricted to which shell you use to keep control of the beast; several shells have been created in the 40-some odd years that Unix has been around. They all do the same job, but each is unique in the features it provides and the syntax it supports.

A Brief History of Shells

The first shell (sh), dubbed the Bourne Shell in honour of its creator Stephen Bourne, was developed at AT&T Bell Labs to accompany the first release of the Unix operating system. Since it was the first shell developed, the Bourne Shell serves as the foundation for all shells that have been created since.

The Bourne Again Shell (bash), whose name is a pun on its ancestor, is fully compatible with the Bourne Shell and includes extra features such as job control, command-line editing, and increased scripting support; consider it to be the Bourne Shell's official replacement. Developed and maintained by the Free Software Foundation, bash's source is freely available, which allows you to compile it on just about any flavour of Unix.

So if you make the jump to Linux or *BSD chances are bash will be waiting there to accept your command when you open up Konsole or Gnome Terminal for the first time.

I Can't Tell the Difference. Can You Tell the Difference?

Windowing systems, such as Aqua, KDE, and Gnome all have menus, icons, windows, and a cursor to dictate your actions to the underbelly of your computer. But each has its own unique quirks (for better or for worse) when compared to the others, in ways that are more than just superficial. For example, Aqua's sheets provide an alternative method of implementing dialog boxes for multiple document applications such as web browsers and word processors, since the user is still able to use another window while the dialog is present.

In much the same way, shells each have their own unique quirks that differentiate themselves, yet each accomplishes the same function: to relay your commands to the big hairy beast in your computer, and in turn, to ferry the results back to you. This is why most of the commands included in your ~/.tcshrc now fail when you try to execute them in bash, since bash uses a different syntax for many of its equivalent commands.

The Kitchen Sink

In addition to the other Unix components of the operating system, Panther installs five shells, all of which are stored in the same location as most other executable programs on Mac OS X, /bin:

Panther's shells: sh, bash, csh, tcsh, and zsh
Figure 1: The shells included with Panther are sh, bash, csh, tcsh, zsh.

Judging by the sizes of the files, it would appear as though bash and sh are in fact identical, while tcsh and csh are identical; this meshes with the introduction to the different shells discussed earlier, since bash is backwards compatible with sh (as is tcsh with csh).

It is important to note that you are by no means limited to using the shells in the above list; several other shells, such as the Korn shell (ksh) can be downloaded and compiled on Mac OS X (or any other Unix system). However, the vast majority of Panther users will likely find the selection more than enough for their needs; if your shell of choice isn't included, chances are you've already downloaded the tarball and compiled it.

Making the Switch

Mac OS X provides several ways to change your default login shell. And given OS X's split personality of Unix workstation and friendly desktop computer, it should be no surprise that one method is accomplished through the shell itself, while the other is accessible to anyone with a (one button) mouse.

Via the Interface

Oddly enough, it's possible to try to change your default login shell without actually knowing what a shell is. Whether or not that's a good thing is up in the air, but to do it just open Terminal's preferences window and enter the path of the shell in the "Execute this command" text field, as shown below:

Terminal Preferences
Figure 2: Using Terminal's preferences window to switch to bash.

Then quit Terminal, and restart it to allow the new settings to take effect.

Via the Shell

The above method of changing your shell is a convenience provided by Apple. But, like many configuration options that apply to the Unix underbelly of Mac OS X, it is merely a graphical front-end to a Unix command that can be invoked in Terminal.

Back in the days before windows and mice, everything had to be done through the command-line -- scary, isn't it? On most Unix systems, changing your shell to bash would be done through the command chsh bash. However, Apple has slightly modified the chsh command in OS X (more specifically, Darwin) so that we must now use chsh -s bash to accomplish this task on our candy-coated machines:

The results of attempting to change your shell, both successful and unsuccessful.
Figure 3: The results of attempting to change your shell, both successfully and unsuccessfully.

Again, restart Terminal to ensure that your changes have taken effect.

Working with bash

Whether you've gone ahead and changed your shell or Panther set it up automatically, you are now working with bash; you can even double-check by checking the results of the command echo $SHELL in Terminal.

Because of the size and complexity of bash, we won't be able to cover all of its features here. Instead, we'll take a look at the most common stumbling blocks that people find when moving from tcsh to bash and focus on those. So let's get started.

Customizing Your Environment

Every time you log in to your computer, bash will look for initialization files that are used to customize your environment, in exactly the same way that tcsh and the other shells do. Thus, instead of executing the same commands every time you log in, you can place these commands in your initialization file so that bash will do the grunt work for you. bash looks for these files in a few different locations, with each having its own distinct purpose:

File Description
/etc/profile system-wide configuration file for the bash and sh shells
~/.profile executed every time a new shell is created, such as when you open a new Terminal window
~/.bash_logout executed when you log out from a shell

Thus, the commands that you wish to invoke every time you open a Terminal should be placed in your profile, located at ~/.profile (you'll have to create it if it doesn't exist). If you wish to have this command executed for every account on your computer that uses bash, place it in the /etc/profile file instead.

A sample initialization file follows:


# shell variables
PS1="\n$PS1 "
PS2=": "

# environment variables
export CVSROOT=/usr/local/cvsroot
export CVS_RSH=/usr/bin/ssh
export JAVA_HOME=/Library/Java/Home
export ANT_HOME=/usr/local/ant
export CLASSPATH=/Users/dave/FVL/xerces.jar

# the path is initially set to "/bin:/sbin:/usr/bin:/usr/sbin"
export PATH=$PATH:/usr/local/bin:/usr/local/mysql/bin

# aliases
alias l="ls -l"
alias ll="ls -al"
alias mckoi="java com.mckoi.runtime.McKoiDBMain"

If you've played around with shell variables and aliases in tcsh, then the commands shown above should look pretty familiar. You'll need to "refresh" your profile after every edit so that the latest changes take effect (think of refreshing your web browser to get the latest copy of a web page). This can be done with the command source filename; so in the case of your log out script, the command source ~/.bash_logout would do the trick.

Shell Variables

A variable, when speaking of programming languages, serves as a placeholder for information that will be retrieved at some point later in the program, such as a number, string, or date. When applied to shells (which actually are programming languages), variables may store your current directory, your prompt string, or any other piece of information required by the tools and programs you use. bash has two types of variables: "regular" variables and environment variables.

Regular Variables

A regular variable typically holds a piece of information that you use frequently. Because variables in shell scripts are loosely typed, unlike C and Java, variables can hold virtually anything: a number, string, or composite value such as an array or hash. Creating a variable is pretty painless: name=value, and retrieving its value is almost as easy: $name.

Thus, if you wished to see the current date when opening a new Terminal window, you could place the following lines in your profile:


mydate=`date "+%H:%M:%S %m/%d/%y"`
echo "hi $USER, the current time is $mydate"

For more information on shell scripting, refer to Chris Stone's series of articles mentioned in the introduction.

Environment Variables

Environment variables are essentially a special case of shell variables; they are visible to all child processes that are spawned from the shell. In layman's terms, this means that applications and scripts that are executed from within shell can read the value of the shell's environment variables; as a result, command-line tools typically use environment variables for configuration settings.

By convention, the names of environment variables are all uppercase. Environment variables on OS X may include the following:

Variable Name Description Example
PATH A list of colon-separated directories where the shell will look for executable files /bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin
CLASSPATH A list of colon-separated directories and/or JAR files that contain Java class files that may be required in the Java Runtime Environment. /Users/dave/FVL/Java/xerces.jar:
/Users/dave/FVL/mckoi.jar
JAVA_HOME The root directory of your Java installation. /Library/Java/Home
PWD Your current directory in the file system. /Applications
USER Your login name. dave

To find out which environment variables have been defined in your shell, use the env command, which simply echoes all the environment variables to the terminal:

All environment variables defined in my shell.
Figure 4: All environment variables defined in my shell.

Creating new environment variables is pretty simple; bash uses the syntax export VAR_NAME=value. Thus any shell variable can be turned into an environment variable via the export command:


# create the JAVA_HOME environment variable
export JAVA_HOME=/Library/Java/Home

# do the same thing explicitly, using two lines
JAVA_HOME=/Library/Java/Home
export JAVA_HOME

And what happens if a required environment variable doesn't exist? Nothing too drastic -- the kernel won't panic and your computer won't start blowing smoke. It shouldn't, anyway. The shell and related tools require certain variables to be defined (such as ), but other tools may need their own environment variables. In the event that you are missing a required variable, chances are the related program will fail safely and let you know what needs to be defined, as does Apache's fop:

A typical example of missing an environment variable.
Figure 5: A typical example of missing an environment variable.

The following table lists the differences in working with variables (both shell and environment variables) between tcsh and bash on OS X:

Command tcsh bash
Display all shell variables set set
Create a shell variable set name=value name=value
Remove a shell variable unset name unset name
List all environment variables env env
Create an environment variable setenv name value export name=value
Remove an environment variable unsetenv name unset name

Aliases

Aliases have been a part of Mac OS for several generations now. However, the terminology has gotten a bit confusing with Mac OS X since it's a branch between two traditionally distinct family trees: Unix and Mac OS, each with their own vocabulary. So it shouldn't be too much of a surprise that bringing the two together results in some conflict. One example of this is the term alias: the Finder and Unix underbelly each use the term for two different things:

Aliases are declared in a similar method to variables, with the keyword alias in front of the declaration: alias name=command. If the command contains spaces, then you will have to wrap it in double quotes. To find out which aliases you have already defined, simply enter the command alias (in the event that you wish to remove an alias, then you can simply use the command unalias name):


alias l="ls -l"
alias ll="ls -al"
alias ~="cd ~"
alias md=mkdir
alias mckoi="java com.mckoi.runtime.McKoiDBMain "

# remove the alias named md (for demonstration purposes)
unalias md

The following image illustrates the use of the first two aliases created above:

An example of how even simple aliases can save you time at the keyboard.
Figure 6: An example of how even simple aliases can save you time at the keyboard.

Setting Your Prompt

After opening a Terminal window and being greeted by the message of the day, the first thing you'll see is your command prompt, waiting to accept your every wish. If you went ahead and changed your prompt in Jaguar with tcsh, chances are you'll be disappointed when you first see Panther's default prompt, which should appear something like:

Panther's default prompt
Figure 7: The default prompt for bash in Panther.

Like tcsh, however, it is a relatively trivial process to change your prompt in bash by setting the value of two special shell variables, PS1 and PS2. Why two, you may ask? Good question -- PS1 is can be considered to be the primary prompt, which is displayed every time a new command can be entered; while PS2 is the secondary prompt that is displayed when a command spans more than one line. This screen shot illustrates the two prompts in use:

Illustration of PS1 and PS2
Figure 8: An illustration of PS1 and PS2 in bash's default configuration.

The following table lists the information that you may wish to insert into your prompt:

Command Description Example
\a ASCII bell character --
\d the current date Sun Feb 08
\H hostname Ginger.local
\h shortened hostname Ginger
\u your username dave
\w current working directory /Applications/Network
\W basename of the current working directory Network
\T current time (12-hour HH:MM:SS format) 01:16:49
\t current time (HH:MM:SS format) 13:16:49
\@ current time (12-hour AM/PM format) 1:16 PM
\n new line --
\\ print a backslash \

From the information listed in the above table, we can see that Panther's default prompt strings are created using the following commands:


PS1="\h:\w \u$ "
PS2=" > "

Wrapping Up

This article provides an overview on making the transition from tcsh and bash; at this point you should now know enough about bash to start tailoring your Panther environment to your needs. And while this article covers several key features of it, we have barely scratched the surface; command history, job control, scripting, and several other features went untouched. For more information, be sure to check out the following resources:

David Miller is combining his passions of photography and working on the web at iStockphoto; when not hacking away with a text editor and a few web browsers in hand, he can be seen huddled over his laptop tweaking levels and curves for his freelance photography. Keep track of David's latest projects over at his home on the web.


Return to the Mac DevCenter

Copyright © 2009 O'Reilly Media, Inc.