Bash tips for power users

Command line

If you're a power Linux user, you probably spend a lot of time in the Bash shell. But with the addition of some functions, autocompletion, configuration tweaks and shortcuts, we reckon we can make you even more productive. You doubtless already have aliases set up in your .bashrc file – shortening commands that you use frequently. Aliases are limited, though, in that (at least in Bash and related shells) you can't pass arguments in from the command line. Functions enable you to do this, so you can minimise your typing further still.

To help you speed up LDAP searches a bit you could use this:

function lds() { ldapsearch “($1=$2)”; }

either on the command line or in your .bashrc. Then type:

lds cn jkemp

and you'll do an LDAP search for any entries with a cn value of jkemp (ldapsearch “(cn=jkemp)” ). This saves lots of typing, and the nuisance of remembering which way round the “ and ( go.

Here's a section of an example .bashrc file, showing a couple of functions and some history setup alterations.

Here's a section of an example .bashrc file, showing a couple of functions and some history setup alterations.

You can use a function to do pretty much anything you can write a shell script to do. You can even get complicated and self-recursive. The following code will multiply an arbitrary-length set of numbers (eg times 3 4 5 will output 60). Note the need to escape the * in the line that calls expr; without this, the shell will expand * to the list of all files, rather than treating it as the multiplication operator.

function times() {
   if [ -z “$2” ]; then
      echo $1
   else
      a=$1
      shift
      b = `times $@`
      echo `expr $a \* $b`
   fi
}

Extending autocompletion

Another trick you'll already be familiar with is hitting tab to auto-complete filenames and commands. This is great, but there are a few ways to extend it further. The first part of this isn't exactly autocompletion, but it helps you locate directories more easily. $CDPATH does for cd what $PATH does for executables. In other words, for a directory in your $CDPATH, you can type just its name and Bash will take you straight there – saving you from having to type the full path.

So, I regularly want to go to the “research” subdirectory of my website. My $CDPATH looks like this:

.:/home/jkemp:/local/www/html/research

So I can just type cd research and I'm there. Note that you need to include the initial full stop (.) in the $CDPATH, or you won't be able to cd to subdirectories without putting ./ at the start of them.

However, this can be a nuisance if I want to go to a subdirectory elsewhere called research-notes. If I'm in the parent of that, and I type research, Tab and hit Enter without concentrating, I'll end up in /local/www/html/research by accident. You'll need to concentrate to make good use of this one!

Programmable completion

OK, so $CDPATH can be useful. But the major improvement to the regular autocompletion is programmable completion. You can write completion functions all by yourself, but to avoid extensive wheel-reinvention, I recommend downloading /etc/bash_completion from www.caliban.org/bash (also available as a package for most distros), and then sourcing that file in your .bashrc (with the line source /etc/bash_completion).

Done? OK, open a new terminal window, then try typing ssh at the command line, then hit Tab. You'll be shown a list of hosts that you might want to ssh to, based primarily on your known_hosts file. A bunch of other commands will do the same thing – try traceroute or ftp.

What about extending this to enable host autocompletion for scripts of your own? If you take a look at /etc/bash_completion, it has a bunch of subroutines beginning with an underscore (such as _known_hosts), and then a line that looks like this:

complete -F _known_hosts traceroute ping telnet host ssh

So if you want to add your command to that list, just add it on the end after ssh, re-source the file, and you should be good to go.

-F is an option that identifies the function to use for completion – anything returned by the function (here, _known_hosts) is a possible completion option. -C is a similar option that executes a command and takes the output from that as possible completion. So what if you want to write your own completion function? Here's a very basic sample function that you can add to /etc/bash_completion, which looks up a partial hostname in LDAP.

_ldapcomplete() {
  COMPREPLY=()
  cur=${COMP_WORDS[COMP_CWORD]}
  output=`ldapsearch -Q “(&(cn=$cur*) (objectClass=ipHost))” cn | grep ^cn:`
  name=${output#* }
  COMPREPLY=( ${COMPREPLY[@]} $name )
  return 0
} 
complete -F _ldapcomplete ssh

The first line sets up the value that'll be used for the completion return (COMPREPLY). Next, $cur is set to the value that's been passed into the function, ie, what's already been input on the command line. The COMP_WORDS variable is set up earlier in /etc/bash_completion for use by all functions. So $cur is what we're trying to complete.

The ldapsearch line looks for an entry in the LDAP directory whose name starts with our existing input, and which is a host-type object, and then strips out all the extraneous LDAP information. $output will look like:

cn: hostname

which is no good for completion as it stands. So the line after that splits on whitespace and takes the second half of $output, to give just hostname. This value is put into COMPREPLY, and the function finishes. In fact, this case deals with only one value, so the line could be

COMPREPLY=$name

But to match the rest of the file, and to allow for later improvement, we treat it as an array.

To use the function, the complete -F _ldapcomplete ssh line is needed: this tells the shell that when using ssh. This particular function should be used for hostname completion. I've already mentioned the major drawback of this function: it doesn't deal with multiple return possibilities, so it will successfully autocomplete only when LDAP returns a single value (otherwise it produces errors). This is your chance to experiment a bit; try fixing it so that it deals properly with multiple returns.

Custom command-line completion in action - this looks up LDAP hosts when Tab is pressed.

Custom command-line completion in action - this looks up LDAP hosts when Tab is pressed.

Repeating history

The Bash history is very useful to avoid having to remember complicated commands. Ctr+R searches backwards through your history incrementally (so to find something referring to a filename that starts with dsl, type dsl then hit Ctrl+R), or the arrow keys will move backwards through the history line by line. However, the existence of multiple identical commands can be really annoying when you're searching through your history. Try setting this:

export HISTCONTROL=ignoreboth

...to leave consecutive duplicate commands out of your history altogether. This variable can also take other values: erasedups will remove all matching commands, not just ignore consecutive ones; and ignorespace will ignore lines that begin with a space.

If you often have multiple terminal windows open at the same time, try adding this line to your .bashrc file:

shopt -s histappend

This makes multiple shells all write to the same history file – avoiding the problem whereby you log out of multiple shells and only get the history of the last one. You can also expand your history with the line:

export HISTSIZE=1000000 HISTFILESIZE=1000000

...so that you have access to a much larger chunk of your past command history.

As you can see, there's a lot more to Bash than you might have been aware of. It's well worth spending some time with the man page – it's surprisingly easy to read. Keep learning new things and improve your shell experience further!

Some useful Bash shortcuts

  • Ctrl+R and Ctrl+S Incremental search backwards and forwards, respectively, in your command-line history.
  • Ctrl+J and Ctrl+G: These useful commands abandon an incremental search, either with the line found, or with the original line, respectively.
  • Alt-. or ESC then . Both insert the final argument to the last command at the cursor point. Useful when moving files around and editing them.
  • Ctrl+A and Ctrl+E Move to the beginning and the end of the command line respectively.
  • Alt+C+Y Insert the first argument to the previous command at the cursor point. If you want the nth argument, hit Alt+N beforehand. So Alt+2 Alt+C+Y will give you the second argument to the previous command. That's a lot of keys to remember, but it's useful once in a while.
  • Ctrl+U Deletes from the cursor to the beginning of the line. Also works when entering passwords, so it's useful when you get stuck halfway and forget where you've got to. If you want to use the line again, use C+Y.
  • Ctrl+T and Alt+T transpose characters and words respectively. Pulls the character/word behind the cursor over the character/word at the cursor. Unfortunately, in at least some terminals (such as Gnome Terminal) Alt+T is overridden, so this may not work for you.
  • Ctrl+W and Alt+backspace Both delete the word behind the cursor; but Ctrl+W uses whitespace as a boundary, whereas M+backspace uses non-alphanumeric characters. So if you have file.txt and hit Ctrl+W, you'll delete the lot; Alt+backspace will leave you with file.. (This also works with underscores.)
First published in Linux Format

First published in Linux Format magazine

You should follow us on Identi.ca or Twitter


Your comments

Great!

Highly useful commands. I've always wanted to use the same history file!

Dont forget !! and !$

I use these commands at least once per fortnight!

/troll

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Post new comment

CAPTCHA
We can't accept links (unless you obfuscate them). You also need to negotiate the following CAPTCHA...

Username:   Password:
Create Account | About TuxRadar