Getting the most from your shell

Unix Insider –

While the rest of the world points and clicks in a world of icons, we in the world of Unix get to use a good old-fashioned command-line interface (CLI). One reason the command line has remained so pervasive in Unix environments is that its implementation -- the Unix shell in its various incarnations -- is very good. With it, the user can build new tools with the ones provided.

To keep things simple, I'll talk about three shells in particular, all in the Bourne shell tradition:

<font face="Courier">/bin/sh</font>
,
<font face="Courier">/bin/ksh</font>
, and
<font face="Courier">/bin/bash</font>
. I'll try to focus on features that all three share, though inevitably I'll have to discuss features that the older shell doesn't have, notably job control and command-line editing.

Command-line editing

The first way to make the shell easier to use is to set up command-line editing. Two types are commonly available in shells derived from the Korn shell: those using the

<font face="Courier">vi</font>
commands, and those using Emacs editing keystrokes. They are very different, and users who swear by one will take pains to avoid the other.

In both

<font face="Courier">ksh</font>
and
<font face="Courier">bash</font>
, to activate
<font face="Courier">vi</font>
command-line editing, type:

<font face="Courier">
set -o vi
</font>

This allows you to use the same set of command keys you have available in

<font face="Courier">vi</font>
.

In both

<font face="Courier">ksh</font>
and
<font face="Courier">bash</font>
, to activate Emacs command-line editing, type:

<font face="Courier">
set -o emacs
</font>

This allows you to use a subset of Emacs command keys, and treat the command line as a sort of one-line Emacs buffer.

Shell options

Setting which type of command-line editing you prefer is just one instance of setting the shell options that control many aspects of your shell's behavior. You set shell options using

<font face="Courier">set -o</font>
. Options can be unset using
<font face="Courier">set +o</font>
.

The man page for your shell will list all the options; here, I'll focus on a few that I think you'll find useful.

  • <font face="Courier">notify</font>
    : Prints job-notification messages asynchronously, instead of just before the prompt. Both
    <font face="Courier">ksh</font>
    and
    <font face="Courier">bash</font>
    support this option.
  • <font face="Courier">noclobber</font>
    : In both
    <font face="Courier">ksh</font>
    and
    <font face="Courier">bash</font>
    , this prevents
    <font face="Courier">></font>
    redirection from overwriting existing files. With this set,
    <font face="Courier">>|</font>
    must be used to force an overwrite, or else the shell will tell you
    <font face="Courier">cannot create [file]: File exists</font>
    .
  • <font face="Courier">bgnice</font>
    : Runs background jobs with lower priority. This is in
    <font face="Courier">ksh</font>
    , but not in
    <font face="Courier">bash</font>
    .
  • <font face="Courier">ignoreeof</font>
    : Determines that shell will not exit on end-of-file (
    <font face="Courier">^D</font>
    );
    <font face="Courier">exit</font>
    must be typed. To avoid infinite loops,
    <font face="Courier">pdksh</font>
    will exit if
    <font face="Courier">EOF</font>
    is read 13 times in a row. The implementation in
    <font face="Courier">bash</font>
    is more sophisticated. To quote the man page:

    "If set, the value is the number of consecutive

    <font face="Courier">EOF</font>
    characters typed before
    <font face="Courier">bash</font>
    exits. If the variable exists but does not have a numeric value, or has no value, the default value is 10. If it does not exist,
    <font face="Courier">EOF</font>
    signifies the end of input to the shell."

  • <font face="Courier">nohup</font>
    : Does not kill running background jobs when a login shell exists. This is another
    <font face="Courier">ksh</font>
    option that isn't found in
    <font face="Courier">bash</font>
    .

Aliases

How do we use the shell? Our most common use is for typing commands, one after the other:

<font face="Courier">
$ vi article
...
$ cat todo
...
$ date
</font>

and so on. When the command takes some typing effort, it's worthwhile to make a shortcut by using what we call an alias. Making an alias is straightforward. At the simplest level, it's as though we're replacing one command with another. Here is an example from my own system:

<font face="Courier">
alias pg=less
</font>

This saves me two keystrokes every time I want to examine a file using the

<font face="Courier">less</font>
pager. Trivial? Maybe. But it's something I use many times each day.

An alias is a way of giving a nickname to a command or a series of commands. The left-hand token is expanded to the right-hand value by the shell, the same way shell wildcards are expanded. So, for example, if I define the alias:

<font face="Courier">
alias rm='rm -i'
</font>

then when I type

<font face="Courier">
$ rm foobar
</font>

the shell actually runs

<font face="Courier">
$ rm -i foobar
</font>

which prompts me before doing anything rash like, oh, removing a file.

Aliases are also a handy way of running a short series of shell commands that might otherwise have to be put in a shell script. If you only have a few commands to run, consider implementing them as an alias rather than a script.

Aliases are inherited. That is to say, in

<font face="Courier">
alias vi='vi -F'
alias vp="vi $HOME/.profile"
</font>

the

<font face="Courier">vi</font>
in
<font face="Courier">vp</font>
will be expanded to
<font face="Courier">'vi -F'</font>
.

Here is a useful way of seeing how much work I do:

<font face="Courier">
alias la='ls -tr1 $HOME/articles/wip'
</font>

Aliases save one from having to remember arcane syntax. Why bother with

<font face="Courier">chmod +x</font>
or
<font face="Courier">chmod 0755</font>
when a two-character alias does what you want?

<font face="Courier">
alias cx="chmod +x $*"
</font>

Sometimes one grows used to certain commands. On some flavors of Unix, for example, the pager program is neither

<font face="Courier">more</font>
nor
<font face="Courier">less</font>
, but
<font face="Courier">pg</font>
. Why bother getting used to typing a new command?

<font face="Courier">
alias pg='less'
</font>

Something I find useful is to have my browser point to a default location if I don't give a URL on the command line.

<font face="Courier">
alias lynx="lynx -cache 100 ${1:-$HOME/homepage/index.html}"
</font>

Functions

The shell is a programming language, so it's only natural that it should have functions. The syntax for functions is simple. A function defined in the shell looks similar to the way it would be defined in C: the function name, followed by a pair of parentheses, then the body of commands that make up the function enclosed in braces.

Functions are a natural progression from aliases, and they avoid the overhead of a shell script at the cost of a bigger environment. This is as good a place as any to mention that this stuff doesn't come free. The more you do in your

<font face="Courier">init</font>
files -- setting variables, defining functions -- the bigger your environment (an area of memory that each new process you start will copy) gets. For example, on my system,

<font face="Courier">
$ env | wc -c 3441
</font>

is 3,441 bytes. You shouldn't notice performance degradation on a modern machine, but it's something to be aware of. If most of your environment is only for interactive use, make sure it isn't defined for noninteractive shells. You can do this by adding the lines:

<font face="Courier">
case $- in
        *i*) ;;
           *) return 0;;
esac
</font>

Here's a little set of functions I find useful for simplifying job control:

<font face="Courier">
b()
{
    bg %$1
}

f()
{
    fg %$1
}

k()
{
    kill %${1:?must give job number}
}

twep()
{
    kill -9 ${1:?need process number for termination with extreme prejudice}
}
</font>

What have we here? Well,

<font face="Courier">b</font>
puts a job in the background,
<font face="Courier">f</font>
brings it into the foreground again, and
<font face="Courier">k</font>
kills it -- all using handy job numbers rather than long-winded process numbers. Finally,
<font face="Courier">twep</font>
is a bit of whimsy. Notice that arguments are passed to functions using the standard shell convention of
<font face="Courier">$n</font>
, where
<font face="Courier">n</font>
is a digit one through nine. Unlike C, the arguments aren't declared between the parentheses following the function name. In fact, those parentheses are really just a bit of syntactic sugar.

The boundaries between aliases, functions, and shell scripts are blurred. Given aliases and functions, jumping to shell scripting is not difficult.

My final example is a complicated function that could just as well be put in an executable file and called a shell script.

The MH mailer stores draft emails as files in a directory, just as it stores all other emails. One then uses

<font face="Courier">comp -use<filename></font>
to work on a particular draft. However, I often have no intention of sending an email straight away, and would rather just fire up my editor on the message file. So I wrote a simple script adding a new command to MH. The script works the same as other MH commands, taking as its argument the number of a mail message to edit. So now I can look at my
<font face="Courier">drafts</font>
directory using the
<font face="Courier">drafts</font>
command, then edit the message I want with
<font face="Courier">vd 20</font>
. Here's the script:

<font face="Courier">
vd()
{
case $1 in
    "");;
    last) vi $(ls $HOME/Mail/drafts/[1-9]*|sort -t '/' +5 -n|tail -1) ;;
    first) vi $(ls $HOME/Mail/drafts/[1-9]*|sort -t '/' +5 -n|head -1) ;;
    [0-9]*) vi $HOME/Mail/drafts/$1;;
    *) echo 'vd: usage: vd [first|last|<msg-nr.>]';;
esac
}
</font>

This is simple programming, using a case statement to decide what to do.

<font face="Courier">$1</font>
, as we know, is the first argument to the function, and depending on its value --
<font face="Courier">last</font>
,
<font face="Courier">first</font>
, a number, or anything else -- we execute the appropriate line of shell commands. Then, skipping the rest, we end up at the end of the function. Those shell commands look complex, but breaking them down, they become clear. The lines for
<font face="Courier">last</font>
and
<font face="Courier">first</font>
are the most complicated, and because they are very similar, we'll consider them together.

First we build a list of path names from the

<font face="Courier">draft</font>
directory:

<font face="Courier">
ls $HOME/Mail/drafts/[1-9]*
</font>

Then we sort them in numerical order on the file name:

<font face="Courier">
sort -t '/' +5 -n
</font>

<font face="Courier">-t</font>
identifies the field separator,
<font face="Courier">+5</font>
says fourth field (we start counting with 0), and
<font face="Courier">-n</font>
says sort numerically.

Next we grab either the last or the first line of this list -- the first or last draft email, respectively.

<font face="Courier">
tail -1
OR
head -1
</font>

Now we want to run the editor on it by passing the output of our long command to the argument list of the editor command, enclosing the whole thing in

<font face="Courier">$(...)</font>
.

The other command lines are much simpler. If we're given a numerical argument, we just build the pathname and hand it to

<font face="Courier">vi</font>
directly.

A note on init files

Of course, we don't type in our aliases and functions anew each time we start up the shell. An interactive shell reads a number of

<font face="Courier">init</font>
files on startup, and that's the best place to put our definitions.

What

<font face="Courier">init</font>
file goes where depends on the shell you're using. In the beginning, there was the Bourne shell,
<font face="Courier">/bin/sh</font>
. You won't find this on your Linux box; there,
<font face="Courier">/bin/sh</font>
is really
<font face="Courier">/bin/bash</font>
, the GNU Bourne Again shell. This had just one
<font face="Courier">init</font>
file,
<font face="Courier">~/.profile</font>
.

1 2 Page 1
Page 1 of 2
ITWorld DealPost: The best in tech deals and discounts.
  
Shop Tech Products at Amazon