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

Introduction

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.

History

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 Terminal.app), 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

...my 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 192.168.76.84
285  Jul 25 14:39:53: ssh marczak@192.168.76.84
287  Jul 26 09:06:04: ssh 192.168.125.164
289  Jul 26 11:10:58: ssh marczak@192.168.76.84
295  Jul 26 16:27:13: man ssh
296  Jul 26 23:10:19: ssh mm@db.wheresspot.com
298  Jul 27 09:19:18: ssh mm@db.wheresspot.com
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@192.168.76.84" line - history id 285 - I can type this:

!285

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}'
28976
$ 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.

Readline

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
$endif

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

CDPATH

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:

CDPATH=".:~:/Users/Shared"

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:

#!/bin/bash
function sum() {
  total=$(($1+$2))
  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:

_dscl() 
{
    local cur prev opts
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    opts="-read -readall -readpli -list"
    if [[ ${cur} == -* ]] ; then
        COMPREPLY=( $(compgen -W "${opts}" — ${cur}) )
        return 0
    fi
}
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 http://www.caliban.org/files/bash/bash-completion-20060301.tar.gz. 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.

 
AAPL
$467.36
Apple Inc.
+0.00
MSFT
$32.87
Microsoft Corpora
+0.00
GOOG
$885.51
Google Inc.
+0.00

MacTech Search:
Community Search:

Software Updates via MacUpdate

VueScan 9.2.23 - Scanner software with a...
VueScan is a scanning program that works with most high-quality flatbed and film scanners to produce scans that have excellent color fidelity and color balance. VueScan is easy to use, and has... Read more
Acorn 4.1 - Bitmap image editor. (Demo)
Acorn is a new image editor built with one goal in mind - simplicity. Fast, easy, and fluid, Acorn provides the options you'll need without any overhead. Acorn feels right, and won't drain your bank... Read more
Mellel 3.2.3 - Powerful word processor w...
Mellel is the leading word processor for OS X, and has been widely considered the industry standard since its inception. Mellel focuses on writers and scholars for technical writing and multilingual... Read more
Iridient Developer 2.2 - Powerful image...
Iridient Developer (was RAW Developer) is a powerful image conversion application designed specifically for OS X. Iridient Developer gives advanced photographers total control over every aspect of... Read more
Delicious Library 3.1.2 - Import, browse...
Delicious Library allows you to import, browse, and share all your books, movies, music, and video games with Delicious Library. Run your very own library from your home or office using our... Read more
Epson Printer Drivers for OS X 2.15 - Fo...
Epson Printer Drivers includes the latest printing and scanning software for OS X 10.6, 10.7, and 10.8. Click here for a list of supported Epson printers and scanners.OS X 10.6 or laterDownload Now Read more
Freeway Pro 6.1.0 - Drag-and-drop Web de...
Freeway Pro lets you build websites with speed and precision... without writing a line of code! With it's user-oriented drag-and-drop interface, Freeway Pro helps you piece together the website of... Read more
Transmission 2.82 - Popular BitTorrent c...
Transmission is a fast, easy and free multi-platform BitTorrent client. Transmission sets initial preferences so things "Just Work", while advanced features like watch directories, bad peer blocking... Read more
Google Earth Web Plug-in 7.1.1.1888 - Em...
Google Earth Plug-in and its JavaScript API let you embed Google Earth, a true 3D digital globe, into your Web pages. Using the API you can draw markers and lines, drape images over the terrain, add... Read more
Google Earth 7.1.1.1888 - View and contr...
Google Earth gives you a wealth of imagery and geographic information. Explore destinations like Maui and Paris, or browse content from Wikipedia, National Geographic, and more. Google Earth... Read more

Strategy & Tactics: World War II Upd...
Strategy & Tactics: World War II Update Adds Two New Scenarios Posted by Andrew Stevens on August 12th, 2013 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Expenses Planner Review
Expenses Planner Review By Angela LaFollette on August 12th, 2013 Our Rating: :: PLAIN AND SIMPLEUniversal App - Designed for iPhone and iPad Expenses Planner keeps track of future bills through due date reminders, and it also... | Read more »
Kinesis: Strategy in Motion Brings An Ad...
Kinesis: Strategy in Motion Brings An Adaptation Of The Classic Strategic Board Game To iOS Posted by Andrew Stevens on August 12th, 2013 [ | Read more »
Z-Man Games Creates New Studio, Will Bri...
Z-Man Games Creates New Studio, Will Bring A Digital Version of Pandemic! | Read more »
Minutely Review
Minutely Review By Jennifer Allen on August 12th, 2013 Our Rating: :: CROWDSOURCING WEATHERiPhone App - Designed for the iPhone, compatible with the iPad Work together to track proper weather conditions no matter what area of the... | Read more »
10tons Discuss Publishing Fantasy Hack n...
Recently announced, Trouserheart looks like quite the quirky, DeathSpank-style fantasy action game. Notably, it’s a game that is being published by established Finnish games studio, 10tons and developed by similarly established and Finnish firm,... | Read more »
Boat Watch Lets You Track Ships From Por...
Boat Watch Lets You Track Ships From Port To Port Posted by Andrew Stevens on August 12th, 2013 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Expenses Review
Expenses Review By Ruairi O'Gallchoir on August 12th, 2013 Our Rating: :: STUNNINGiPhone App - Designed for the iPhone, compatible with the iPad Although focussing primarily on expenses, Expenses still manages to make tracking... | Read more »
teggle is Gameplay Made Simple, has Play...
teggle is Gameplay Made Simple, has Players Swiping for High Scores Posted by Andrew Stevens on August 12th, 2013 [ permalink ] | Read more »
How To: Manage iCloud Settings
iCloud, much like life, is a scary and often unknowable thing that doesn’t always work the way it should. But much like life, if you know the little things and tweaks, you can make it work much better for you. I think that’s how life works, anyway.... | Read more »

Price Scanner via MacPrices.net

13″ 2.5GHz MacBook Pro on sale for $150 off M...
B&H Photo has the 13″ 2.5GHz MacBook Pro on sale for $1049.95 including free shipping. Their price is $150 off MSRP plus NY sales tax only. B&H will include free copies of Parallels Desktop... Read more
iPod touch (refurbished) available for up to...
The Apple Store is now offering a full line of Apple Certified Refurbished 2012 iPod touches for up to $70 off MSRP. Apple’s one-year warranty is included with each model, and shipping is free: -... Read more
27″ Apple Display (refurbished) available for...
The Apple Store has Apple Certified Refurbished 27″ Thunderbolt Displays available for $799 including free shipping. That’s $200 off the cost of new models. Read more
Apple TV (refurbished) now available for only...
The Apple Store has Apple Certified Refurbished 2012 Apple TVs now available for $75 including free shipping. That’s $24 off the cost of new models. Apple’s one-year warranty is standard. Read more
AnandTech Reviews 2013 MacBook Air (11-inch)...
AnandTech is never the first out with Apple new product reviews, but I’m always interested in reading their detailed, in-depth analyses of Macs and iDevices. AnandTech’s Vivek Gowri bought and tried... Read more
iPad, Tab, Nexus, Surface, And Kindle Fire: W...
VentureBeat’s John Koetsier says: The iPad may have lost the tablet wars to an army of Android tabs, but its still first in peoples hearts. Second place, however, belongs to a somewhat unlikely... Read more
Should You Buy An iPad mini Or An iPad 4?
Macworld UK’s David Price addresses the conundrum of which iPAd to buy? Apple iPad 4, iPad 2, iPad mini? Or hold out for the iPad mini 2 or the iPad 5? Price notes that potential Apple iPad... Read more
iDraw 2.3 A More Economical Alternative To Ad...
If you’re a working graphics pro, you can probably justify paying the stiff monthly rental fee to use Adobe’s Creative Cloud, including the paradigm-setting vector drawing app. Adobe Illustrator. If... Read more
New Documentary By Director Werner Herzog Sho...
Injuring or even killing someone because you were texting while driving is a life-changing experience. There are countless stories of people who took their eyes off the road for a second and ended up... Read more
AppleCare Protection Plans on sale for up to...
B&H Photo has 3-Year AppleCare Warranties on sale for up to $105 off MSRP including free shipping plus NY sales tax only: - Mac Laptops 15″ and Above: $244 $105 off MSRP - Mac Laptops 13″ and... Read more

Jobs Board

Sales Representative - *Apple* Honda - Appl...
APPLE HONDA AUTOMOTIVE CAREER FAIR! NOW HIRING AUTO SALES REPS, AUTO SERVICE BDC REPS & AUTOMOTIVE BILLER! NO EXPERIENCE NEEDED! Apple Honda is offering YOU a Read more
*Apple* Developer Support Advisor - Portugue...
Changing the world is all in a day's work at Apple . If you love innovation, here's your chance to make a career of it. You'll work hard. But the job comes with more than Read more
RBB - *Apple* OS X Platform Engineer - Barc...
RBB - Apple OS X Platform Engineer Ref 63198 Country USA…protected by law. Main Function | The engineering of Apple OS X based solutions, in line with customer and Read more
RBB - Core Software Engineer - Mac Platform (...
RBB - Core Software Engineer - Mac Platform ( Apple OS X) Ref 63199 Country USA City Dallas Business Area Global Technology Contract Type Permanent Estimated publish end Read more
*Apple* Desktop Analyst - Infinity Consultin...
Job Title: Apple Desktop Analyst Location: Yonkers, NY Job Type: Contract to hire Ref No: 13-02843 Date: 2013-07-30 Find other jobs in Yonkers Desktop Analyst The Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.