TweetFollow Us on Twitter

Mac in The Shell: Customizing the bash Experience Further

Volume Number: 24
Issue Number: 10
Column Tag: Mac in The Shell

Mac in The Shell: Customizing the bash Experience Further

Changing the bash shell to suit your needs

by Edward Marczakr


Last month, I launched into a different kind of introduction to bash. It concerns making you, the end user, comfortable and efficient. This month wraps up this topic with a look at more ways to wring magic out of bash.


For once, I'm not talking about "history" as in a "history lesson." This time, I'm talking about history in bash. bash history is both a command, and a function of the bash shell. bash very nicely will keep history (a log, or journal) of the commands you execute. Open up a shell (probably using, and type ls -l. Now, change into the /Users/Shared directory using the cd command: cd /Users/Shared. From here, press the up arrow on your keyboard. The current input line should recall the previous command (cd /Users/Shared). Pressing the up arrow again will recall the command before that. Do so, and when "ls -l" appears, press the return key to execute the ls command and list the contents of the directory you're currently in. Now, type history, and press return. You should see a list of commands previously executed, ending with "ls -l", "cd /Users/Shared" and another "ls -l".

The simple fact that we can scroll through our session's history with the up and down arrow, and retrieve an entire listing is pretty amazing all on its own. Not surprisingly, it gets better!

First, let's make sure we capture all of the history we want. There are several shell variables that configure the behavior of bash history. Here's what I use in my ~/.bash_profile:

export HISTSIZE=12000
export HISTFILESIZE=12000
export HISTCONTROL='ignoreboth'
export HISTTIMEFORMAT='%b %d %H:%M:%S: '

(there's actually a little more to it, but I'll cover that in due time).

The HISTSIZE variable sets the number of commands to remember in the command history. If not set, it defaults to 500. HISTFILESIZE, when set, will tell bash to truncate the history file, if necessary, by removing the oldest entries, to contain no more than that number of lines. Again, the default value is 500. The history file is also truncated to this size after writing it when an interactive shell exits.

HISTCONTROL is a very useful variable. Assign HISTCONTROL a colon separated list of the following options to alter how history is saved:

ignorespace: lines which begin with a space character are not saved in the history list.

ignoredups: lines matching the previous history entry are not saved.

ignoreboth: enforces both ignorespace and ignoredups.

erasedups: causes all previous lines matching the current line to be removed from the history list before that line is saved.

If HISTCONTROL is not set, all lines read by the shell parser are saved in the history list. If you like to watch a directory by executing "ls -l" and then repeatedly pressing up-arrow-return, ignoredups is for you! This way, you'll only see one "ls -l" in history no matter how many times you repeat the command.

HISTTIMEFORMAT is a variable I see get very little use, but I find immensely useful. Simply, when set, each entry in the history list will have a timestamp save with the entry. With the example setting I give above, my history looks like this:

580  Jul 29 08:25:26: cd dev/objc/
581  Jul 29 08:25:27: ls -la
582  Jul 29 08:34:29: cd dumb

Yes, I have a directory named "dumb." I also have several shell options defined, but the most important relating to history is this:

shopt -s histappend

The histappend option appends the history from the current shell when it exits to the history file. This is useful - I'd claim critical - when using multiple shells. To explain further, shell history is not immediately written to the history file, but is maintained per shell, and written on shell exit. If I have two shells running, the last to exit writes the history file that it maintained. If you only even use a single shell at a time, you wouldn't notice this behavior, but I think it's a nice option to have enabled regardless.

As an aside: I actually use two other shell options: histverify and histreedit. Both affect substitution and shell expansion. Both are also a little outside the scope of this article, however, the bash man page has more information if you're so inclined.

Another aside: I really like setting the cmdhist option, too using:

set cmdhist

This option will save multi-line entries as a single line in history. Multi-line entries are created using the backslash character at the end of a line. For example, with cmdhist set, if I were to enter the following:

$ for i in `ls`; do \
> echo $i; \
> done history would actually contain:

for i in `ls`; do echo $i; done

It's all up to personal taste if you prefer this or not, so, use what works best for you.

More History

Just investigating bash history functionality alone is a deep topic. Mainly because it exists to save you work; always remember that. So, now you can up-and-down-arrow through history, and use the history command to list all history. If you typed a command that you want to recall, you can certainly press up-arrow until you find it. What if that command is way back? Well, you could remember part of the command and use grep to find it:

$ history | grep ssh
272  Jul 25 10:09:56: ssh
285  Jul 25 14:39:53: ssh marczak@
287  Jul 26 09:06:04: ssh
289  Jul 26 11:10:58: ssh marczak@
295  Jul 26 16:27:13: man ssh
296  Jul 26 23:10:19: ssh
298  Jul 27 09:19:18: ssh
330  Jul 27 09:52:21: ssh minoc
331  Jul 27 09:17:26: ssh serveradmin@ballast

(remember - I'm showing an option timestamp, which you likely won't see). From this list, you could copy and paste the line you'd like to repeat (yuck). Or, you can ask bash to expand by the history id. This is called history expansion. For example, if I wanted to repeat the "ssh marczak@" line - history id 285 - I can type this:


and press return. Much easier than retyping the entire line. While using grep to find things in history is OK for a grand overview, there are other, more refined ways of recalling history.

To broaden the scope of the exclamation point operator, in general, after the exclamation point you specify an event designator. A number will recall a specific numbered event in history. Here's a handy list of useful designators:

(numerical value) - recall specific event in history

! - previous line

(text) - previous line starting with given text.

(?text) - previous line containing text

Examples are always best, so, here's one of each (save "numerical value," which you've already seen in action).

If you ssh into a remote machine, do your work, get out and then realize, "oops - I need to do one more thing," simply type !! and press return. This will run the previous command. Remember: this is a substitution, so, you can use "!!" wherever you want to substitute the previous command. An example of this would be:

$ ps ax | grep [W]ord | awk '{print $1}'
$ kill $(!!)

The text designator substitutes the previous match. For example, if I wanted re-run the ps command from the previous example - you know, to make sure it's really dead - typing !ps and pressing return will match that ps, and execute the entire line.

The final designator that I'm going to show matches text anywhere in the event, not just the beginning. To once again rely on the previous example, to now match the ps event, you could type !?ord, or even !?awk, press return and re-execute the entire ps expression.


Developed by GNU, bash uses the readline library for its command input. Simply stated, readline provides a set of functions that allow users to edit command lines as they are typed in. Readline is also responsible for providing the history function to bash (and other applications that choose to include it). Readline is a bit of a discussion in and of itself, so, I'm just going to show one immediately available function, and one tweak that makes all the difference to my history experience.

Readline runs in one of two modes: emacs or vi. The default is emacs mode. This allows you to use emacs key bindings to move about the command line. It's actually the source of the keyboard commands shown last month. From this, we gain reverse search through our history. Press ctrl-r, and your prompt will change to "(reverse-i-search)`':", alerting you that what you type is a search back through history. Each key press finds the best previous match. If what you type fails to match an event in history, you receive a bell (visual or audible, depending on how your session is configured). If you have more than one match, press ctrl-r again to cycle through matches.

Readline is configured using the file .inputrc that it finds in your home directory (you'll need to create this file, as it's not there by default). This allows for further customization and ways to implement features that match the way you work. Here's the .inputrc file that I use:

"\e[B": history-search-forward
"\e[A": history-search-backward
Space: magic-space

The first two lines bind the up and down arrow to searching forward and back through history. You may think the arrows already so this - and they do to a degree - however, with this addition, a little typing goes a long way. With these lines in your ~/.inputrc, if you were to type ssh, and then press the up arrow, you'll navigate back through history only seeing events that match the beginning "ssh". The "magic-space" line causes readline perform expansion each time the space bar is pressed.

One final note: bash isn't the only application that uses readline. This means that changes to .inputrc will affect all applications relying on it. For example, the command-line MySQL client uses the readline library to implement command-line editing and history retrieval. If you want a particular setting to apply only to one environment, .inputrc does understand a limited form of conditionals. So, to cause settings to only apply to bash, use this in .inputrc:

$if bash
Space: magic-space

This conditional will cause the magic-space behavior to apply only to bash. Naturally, any settings can be placed in the conditional.


CDPATH is simply a shell variable. Like the PATH variable, which tells the shell which paths to search for an executable, CDPATH informs the cd command which paths to look in when changing directories. This is a simple way to reduce your typing. CDPATH is specified just like PATH: a list of directories, separated by a colon. If, for example, to search your home directory and /Users/Shared, specify this:


The "dot" up front in that specification tells cd to search the current directory. You probably want to do that, right? Using the CDPATH shown, if there's a directory named "photo" under /Users/Shared, and my present working directory (pwd) is ~/Library (or anywhere, really), simply typing cd photo will change my present working directory to /Users/Shared/photo.

This should tie into good usage of the PS1 variable as described in the previous column, as you should always have a visual clue as to which directory you actually currently in.

The order of CDPATH is important. Just like the PATH variable, first match "wins." Using the previous example, if there was a directory named "photo" in both my home directory and /Users/Shared/, typing cd photo would bring me to the photo directory in my home. Thanks to the current directory specification (".") up front, the current directory always takes precedence, so, you'll always have 'normal' behavior.

Bash Programmable Completion

Modern versions of bash allow programmable completion, extending the standard completion that the Tab key normally provides. Again, this is a topic that can probably take up its own article or chapter in a book. Investigating every aspect of programmable bash completion is beyond the scope of this article, but I need to point out that it exists, and how to get some useful completions running in your shell.

Here's what the bash man page says about standard completion (pressing the Tab key):

"Attempt to perform completion on the text before point. Bash attempts completion treating the text as a variable (if the text begins with $), username (if the text begins with ~), hostname (if the text begins with @), or command (including aliases and functions) in turn. If none of these produces a match, filename completion is attempted."

However, this basic setup can be improved. For example, on an unadorned bash-shell-out-of-the-box on OS X, if you were to type man dsed and press Tab, you'll just receive a bell. Wouldn't it be nice if you could press tab and have the line completed with man dseditgroup? Here's what the bash man page says about programmable completion:

"When word completion is attempted for an argument to a command for which a completion specification (a compspec) has been defined using the complete builtin, the programmable completion facilities are invoked.

First, the command name is identified. If a compspec has been defined for that command, the compspec is used to generate the list of possible completions for the word. If the command word is a full pathname, a compspec for the full pathname is searched for first. If no compspec is found for the full pathname, an attempt is made to find a compspec for the portion following the final slash.

Once a compspec has been found, it is used to generate the list of matching words. If a compspec is not found, the default bash completion as described above under Completing is performed."

A bit wordy, perhaps. However, the upshot is this: you can have bash complete on just about anything. If your company has custom command-line utilities, you could create a completion specification for the valid switches and completions for your specific utility! The key to it all is the complete built-in command.

Last month, we touched on aliases and functions. As a quick refresher, a function is a subroutine, or, a code block that implements a set of operations. This function will add two numbers together and print their result:

function sum() {
  echo "The sum of $1 and $2 is $total"
sum 5 12

As you can see, calling a function is just like executing any bash script: arguments are passed in order into the function. Programmable completion takes advantage of this, allowing you to specify functions that will determine completions for matching text.

As a short example, imagine that we want completions for the dscl command. In the interest of space and clarity, our function will only complete on these options: -read, -readall, -readpl, -readpli and -list. A function that can do this for us would look something like this:

    local cur prev opts
    opts="-read -readall -readpli -list"
    if [[ ${cur} == -* ]] ; then
        COMPREPLY=( $(compgen -W "${opts}" — ${cur}) )
        return 0
complete -F _dscl dscl

If you're anxious to try this now, save this in a file named "dscl" and source it by typing source dscl. Type dscl -r at the command prompt and press the Tab key. bash should complete the "-r" as "-read" for you. Press the Tab key twice, and you'll see that it'll match the other "-read*" entries we supplied. Very cool. How does it work?

We start out by defining some variables: cur, the current match (being typed at the command-line), prev, the previous word typed and opts, which is the list of options to complete. The actual completion is handled by the compgen bash built-in. compgen is used to fill in the COMPREPLY variable. COMPREPLY has special meaning to bash in that it holds the output of a completion.

You can certainly use this function as a template. However, while it works for simple completions, a function could certainly be much more powerful and complex. What if you're just trying to get your completion fix and don't have the time/skills/energy to write an in-depth function for completion?

Fortunately, there are many good bash completion examples full scripts that you can download. One of the best is created by Ian Macdonald, and is available from While originally created with Linux in mind, this comprehensive completion file is ideal for OS X, too. Even better: any Linux-specific commands that it can't find on OS X are simply not loaded, saving memory.

To use it, download the file, unarchive it, and find the bash_completion file at the top level of the resulting directory. Copy that into /etc. Have your bash initialization script source it:

. /etc/bash_completion

("." is a synonym for the source command). If you don't want to wait to login to a new shell, source it right at the command line. You can test it with one of the completions it brings: the cd completion. Wait...doesn't cd already complete with the built-in completion? Well, yes, but it's free to do so in meaningless ways. If you have a file in your home directory named "current-list", and a directory named "current_projects", the unmodified completion will complete the word "current", but then wait for you to clarify. With the programmed behavior, we realize that with the cd command, only one of the choices makes sense - the directory. Now you should notice that typing cd cur followed by the Tab key, you'll get the correct, full completion.

This bash completion file provides a framework for creating completions. If you want to finish off the dscl example above, or, create one for a custom binary or script, create an /etc/bash_completion.d directory, and drop your completion file in there. The /etc/bash_completion script is designed to look in /etc/bash_completions.d and source each file it containes.

One last tip that, although it gets put into ~/.inputrc, it ties in with tab completion: A helpful completion trick is to drop the following line into your .inputrc file:

set show-all-if-ambiguous on

This addition causes readline to show alternate matches immediately, rather than make you press Tab twice.

Wrapping Up

I hope the tips from this and last month have had an impact on your work inside the bash shell. Many times, it's the little things like this that can make a huge difference in your daily work. I certainly remember the first time I saw these in action after using bash for some time. It was a bit of magic! Quite honestly, while we've covered a lot in this and the previous article, there's even more to explore in bash. If this coverage has piqued your interest, investigate the shopt and bind commands along with the many more options available to the readline library.

Media of the month: Gödel, Escher, Bach: An Eternal Golden Braid, by Douglas R. Hofstadter. I recently unearthed my copy of this venerable tome and remembered what a revelation it is. Granted, it's not for everyone, but, if you've always wondered about it, or (especially) if you've never heard of it, go find a copy and dig it.

This will most likely be the last bash-specific Mac in the Shell column for some time. If there are any bash topics that you'd like to see explored deeper (or initially), let me know, and we can dig back in. Next month, we'll get into the wider world of scripting in OS X.

Ed Marczak is the Executive Editor for MacTech Magazine, and has been lucky enough to have ridden the computing and technology wave from early on. From teletype computing to MVS to Netware to modern OS X, his interest was piqued. He has also been fortunate enough to come into contact with some of the best minds in the business. Ed spends his non-compute time with his wife and two daughters.


Community Search:
MacTech Search:

Software Updates via MacUpdate

f.lux 36.1 - Adjusts the color of your d...
f.lux makes the color of your computer's display adapt to the time of day, warm at night and like sunlight during the day. Ever notice how people texting at night have that eerie blue glow? Or wake... Read more
VirtualBox 5.0.2 - x86 virtualization so...
VirtualBox is a family of powerful x86 virtualization products for enterprise as well as home use. Not only is VirtualBox an extremely feature rich, high performance product for enterprise customers... Read more
File Juicer 4.43 - Extract images, video...
File Juicer is a drag-and-drop can opener and data archaeologist. Its specialty is to find and extract images, video, audio, or text from files which are hard to open in other ways. In computer... Read more
Apple MainStage 3.2 - Live performance t...
MainStage 3 makes it easy to bring to the stage all the same instruments and effects that you love in your recording. Everything from the Sound Library and Smart Controls you're familiar with from... Read more
formZ 8.5 - Solid and surface modeler. (...
formZ pro is a powerful 3D design application featuring a variety of modeling personalities and tools with an easy to use interface to express and communicate your imagination. It is based on... Read more
PopChar 7.2 - Floating window shows avai...
We're also selling a 5-license family pack for only $25.99! PopChar helps you get the most out of your font collection. With its crystal-clear interface, PopChar provides a frustration-free way to... Read more
FileMaker Pro 14.0.2 - Powerful, easy-to...
FileMaker Pro is powerful, easy-to-use software used to create custom solutions for your business that run on iPad, iPhone, Windows, Mac and the web. Use FileMaker Pro to manage and share information... Read more
djay Pro 1.2 - Transform your Mac into a...
djay Pro provides a complete toolkit for performing DJs. Its unique modern interface is built around a sophisticated integration with iTunes and Spotify, giving you instant access to millions of... Read more
Stacks 2.7.2 - New way to create pages i...
Stacks is a new way to create pages in RapidWeaver. It's a plugin designed to combine drag-and-drop simplicity with the power of fluid layout. Features Fluid Layout: Stacks lets you build pages... Read more
Vitamin-R 2.30 - Personal productivity t...
Vitamin-R creates the optimal conditions for your brain to work at its best by structuring your work into short bursts of distraction-free, highly focused activity alternating with opportunities for... Read more

ReBoard: Revolutionary Keyboard (Utilit...
ReBoard: Revolutionary Keyboard 1.0 Device: iOS Universal Category: Utilities Price: $1.99, Version: 1.0 (iTunes) Description: Do everything within the keyboard without switching apps! If you are in WhatsApp, how do you schedule a... | Read more »
Tiny Empire (Games)
Tiny Empire 1.1.3 Device: iOS Universal Category: Games Price: $2.99, Version: 1.1.3 (iTunes) Description: Launch cannonballs and blow tiny orcs into thousands of pieces in this intuitive fantasy-themed puzzle shooter! Embark on an... | Read more »
Astropad Mini (Productivity)
Astropad Mini 1.0 Device: iOS iPhone Category: Productivity Price: $4.99, Version: 1.0 (iTunes) Description: *** 50% off introductory price! ​*** Get the high-end experience of a Wacom tablet at a fraction of the price with Astropad... | Read more »
Emo Chorus (Music)
Emo Chorus 1.0.0 Device: iOS Universal Category: Music Price: $1.99, Version: 1.0.0 (iTunes) Description: Realistic Choir simulator ranging from simple Chorus emulation to full ensemble Choir with 128 members. ### introductory offer... | Read more »
Forest Spirit (Games)
Forest Spirit 1.0.5 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0.5 (iTunes) Description: | Read more »
Ski Safari 2 (Games)
Ski Safari 2 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: The world's most fantastical, fun, family-friendly skiing game is back and better than ever! Play as Sven's sister Evana, share... | Read more »
Lara Croft GO (Games)
Lara Croft GO 1.0.47768 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0.47768 (iTunes) Description: Lara Croft GO is a turn based puzzle-adventure set in a long-forgotten world. Explore the ruins of an ancient... | Read more »
Whispering Willows (Games)
Whispering Willows 1.23 Device: iOS Universal Category: Games Price: $4.99, Version: 1.23 (iTunes) Description: **LAUNCH SALE 50% OFF** - Whispering Willows is on sale for 50% off ($4.99) until September 9th. | Read more »
Calvino Noir (Games)
Calvino Noir 1.1 Device: iOS iPhone Category: Games Price: $3.99, Version: 1.1 (iTunes) Description: The film noir stealth game. Calvino Noir is the exploratory, sneaking adventure through the 1930s European criminal underworld.... | Read more »
Angel Sword (Games)
Angel Sword 1.0 Device: iOS Universal Category: Games Price: $6.99, Version: 1.0 (iTunes) Description: Prepare to adventure in the most epic full scale multiplayer 3D RPG for mobile! Experience amazing detailed graphics in full HD.... | Read more »

Price Scanner via

12-inch 1.1GHz Gold MacBook on sale for $1149...
B&H Photo has the 12″ 1.1GHz Gold Retina MacBook on sale for $1149.99 including free shipping plus NY sales tax only. Their price is $150 off MSRP, and it’s the lowest price available for this... Read more
27-inch 3.3GHz 5K iMac on sale for $1849, sav...
Best Buy has the 27″ 3.3GHz 5K iMac on sale for $1849.99. Their price is $150 off MSRP, and it’s the lowest price available for this model. Choose free shipping or free local store pickup (if... Read more
Worldwide Tablet Shipments Expected to Declin...
Does Apple badly need a touchscreen convertible/hybrid laptop MacBook? Yes, judging from a new market forecast from the International Data Corporation (IDC) Worldwide Quarterly Tablet Tracker, which... Read more
Continued PC Shipment Shrinkage Expected Thro...
Worldwide PC shipments are expected to fall by -8.7 percent in 2015 and not stabilize until 2017, according to the latest International Data Corporation (IDC) Worldwide Quarterly PC Tracker data. The... Read more
Imminent iPhone 6s Announcement Leads To 103%...
NextWorth Solutions, with its online and in-store electronics trade-in programs including, reports that it has experienced a 103 percent surge in quoted trade-in values over the... Read more
Weekend Deal: 13-inch Retina MacBook Pros for...
Save up to $100 on the purchase of a new 2015 13″ Retina MacBook Pro at the following resellers this weekend. Shipping is free with each model: 2.7GHz/128GB MSRP $1299 2.7GHz/... Read more
13-inch 2.5GHz MacBook Pro on sale for $999,...
B&H Photo has the 13″ 2.5GHz MacBook Pro on sale for $999.99 including free shipping plus NY sales tax only. Their price is $100 off MSRP. Read more
Apple refurbished iPad Air 2s available for u...
The Apple Store has Apple Certified Refurbished iPad Air 2s available for up to $140 off the price of new models. Apple’s one-year warranty is included with each model, and shipping is free: - 128GB... Read more
The ‘Book Mystique Still Magic After All Thes...
This column has been called The ‘Book Mystique for what has been a 15 year run so far, and I have no interest in changing the name. The reference is to what I’ve observed is a the near magical... Read more
15-inch Retina MacBook Pros on sale for up to...
B&H Photo has 2015 15″ Retina MacBook Pros on sale for up to $184 off MSRP including free shipping plus NY sales tax only: - 15″ 2.2GHz Retina MacBook Pro: $1815 $184 off - 15″ 2.5GHz Retina... Read more

Jobs Board

*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
Software QA Engineer, *Apple* Pay Security...
Changing the world is all in a day039s work at Apple . If you love innovation, here039s your chance to make a career of it. You039ll work hard. But the job comes with Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.