
UnixWorld Online: Tutorial Article: No. 018
UnixWorld Online: Tutorial Article: No. 018
Getting the Most From Your Shell
By Paul Dunne.
Everyone uses it, but do they know how to use it to best advantage? I'm
talking about the Unix shell. No matter whether you log in to a super-duper
latest-thing X desktop or with a Wyse 30 over a slow modem connection, you
will most likely at some point be using the shell. This article looks at
how you can get the most from it.
Questions regarding this article should be directed to the
author at paul.dunne@mailroom.com
While the rest of the world points and clicks in a scary little world of
icons, all alike, we in the world of Unix get to use our good old CLI, or
command line interface. One big reason why the CLI has remained so prevaisive
in Unix environments is that it is actually damn good. Modern Unix shells
are stable and powerful. This article looks at some methods to increase
the power and usableness of your shell.
Introduction
So, I will be looking at how to get more out of your shell. I personally use
a version of the Korn shell, pdksh, but most of what I say will be applicable
to bash; where it isn't, I'll tell you so. As Zsh is mostly compatible with
ksh, most of the article is useful for that as well. I will not bother
with the C shell, on the grounds that I neither use it nor like it, and
that it is so different as to be really the subjet of a seperate article.
This article is not a shell tutorial for novices; it will presume
that you know how to run commands, what wildcards are, and such stuff.
What I will be doing is taking a look at some things that a lot of regular
shell users don't realise can be done.
All Those Dot Files
An interactive shell reads a number of dot files on startup -- one of which,
confusingly, doesn't begin with a dot at all.
Note that commands in these files are "sourced" rather than executed;
that is, commands affect the current evironment (executing a command
doesn't affect the current i.e. parent environment -- this is why shell scripts
to cd to a directory don't affect the current directory of your login shell).
In the beginning, there was the Bourne shell, /bin/sh. By the way, you
won't find this if you go looking on your Linux box; there, /bin/sh is
really /bin/bash, the GNU Bourne-Again SHell. This had just one init file,
~/.profile.
Nowadays, there are at least two profile files for all shells derived from
/bin/sh: one global file in /etc, /etc/profile; and one user-specific file
in the user's home directory, ~/.profile.
Then, there are shell-specific init. files.
For example, with pdksh we have;
~/.kshrc
More properly, this is the default value of the environment variable ENV,
which points to a file to source; we can set ENV to signify any file we like.
In any case, every time a new interactive shell is started, this
file will be sourced.
Similarly, bash has, in addition to the two standard files, a number of
others in the user's home directory.
Bash's use of init files depends on the type of shell it is invoked as.
Bash knows of three types of shell: two interactive, login, and non-login,
and one non-interactive.
For login shells, these rules apply:
if /etc/profile exists, it is sourced. Then, bash looks for several
files in the user's home directory, and sources the first one it finds.
Firstly, it looks for ~/.bash_profile If this file does not exist, it looks
for ~/.bash_login, and sources that instead. Finally, if neither are found,
bash will try to source ~/.profile. On exit, if ~/.bash_logout exists, it
is sourced.
For non-login interactive shells, if ~/.bashrc exists, it will be sourced.
And that's it!
For non-interactive shells, if the environment variable ENV is set, the file
it points to is sourced, as if the command
if [ "$ENV" ];
then
. $ENV;
fi
had been executed. However PATH is not used to search for the
pathname given in ENV.
users of ksh can get a similar functionality to the bash .bash_logout
file by using the following trick. Put this line into .profile:
trap '. $HOME/.sh_logout; exit' 0
Now, when the shell exits, it will source the .sh_logout file. The same
trick can also be used for bash when running as a non-login shell. Put the
line into .bashrc.
Caring About the Environment
We have already seen that the shell may itself refer to environment variables
on start-up. Enviornment variables are also used by umpteen programs, to
find out things as various as what editor to use, where to find a file or
set of files, what pager to use, etc, etc.
Let us start with alook through /etc/profile, to see environment variables
that can usefully be defined globally, for all users.
A useful set of environment variables controls locale.
export LC_ALL='en_IE'
Here, I've set LC_ALL to en_IE, that is, Irish English. The default
value is "C", the portable locale, which equates to the values used
in American English, in effect.
A subset of variables allow fine-tuning of various aspects of
locale support.
LC_COLLATE
LC_CTYPE
LC_MONETARY
LC_NUMERIC
LC_TIME
These are not normally needed.
See the locale manpages for further details.
Before Linux had locale support, different programs made their own
arrangements. Left over from those times, we have such things as:
export LESSCHARSET=latin1
export MM_CHARSET='iso-8859-1'
Which tell less(1) and the MH set of mail programs to use the Latin-1
character set, so that we can correctly display characters such as ý.
export PS1="$ "
export PS2='> '
We set the default values explictly for the shell prompts here.
Here's some special magic for root. This is done in /etc/profile
rather than /root/.profile to save the bother of setting up a
.profile for root. Arguably, as root is not a "real" user, the
account shouldn't have such files anyway. Anyway, now when we
log in as root, we get the special root prompt '#', and a suitably
restricted path.
if [ "$LOGNAME" = "root" ]; then
PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin
PS1="$LOGNAME # "
fi
Some things are better done in the individual user's init. file.
There are a host of variables that the user may wish to set, to
control the behaviour of his shell and other programs.
If you are using X, it is a good idea to set the DISPLAY variable, either
globally or in your .profile. Usually, this will be
export DISPLAY=:0.0
A default editor can be set like so:
export EDITOR=vi
Some commands, such as less(1), look for a suitably-named environment variable
to contain command line arguments. This saves some typing:
export LESS='-c -C -e -g -Q'
export MORE='-c'
Many programs use the program named in PAGER when they need to display lines
of text.
export PAGER=less
You might wish to do some fiddling with the path. Adding the place
where you store your own scripts and programs, for example, like so:
export PATH=/home/paul/bin:$PATH
Changing the Prompt
The default values for the two environment variables that control what your
prompt looks like are usually set in /etc/profile to somthing mundane, such
as:
export PS1="$ "
export PS2='> '
PS1 is the default prompt displayed on each new line. PS2 is the prompt
displayed when more input is needed to compete a command. For example:
$ for i in *
> do
>
But we are not limited to a puny little "$". I like to have some idea of
where I am included inthe prompt, so I put part of the hostname and part of
the path there. Now, the full hostname, not to mention the full path, are too
long, so I do some fancy footwork:
export PS1=`hostname|sed 's/\([^.]\)\..*/\1:/'`'${PWD##*/home/paul}$ '
That looks ferociously complicated, so let's break it down. First, we get
the hostname; but we only want the first part, the host, not the domain,
so we pipe it through sed. Then, we take the working directory (from the
environment variable PWD, which is set automatically), and do some magic on
it using the useful shell construct ${}. As written, this takes the value
of PWD and strips out the given string, /home/paul/, because I already know
I'm there. Finally, we use '$ ' for the "ready" prompt. Examples will make
this clearer.
My basic prompt, in my home directory, looks like
dunne:$
if I cd to a subdir of my home directory the prompt changes to show the
subdir, eg..
dunne:/articles$
If I cd outside my home directory, then and only then do I get he full path,
eg.
dunne:/usr/src$
Aliases
An alias is a way of giving a nickname to a command or series of commands.
The lefthand token is expanded to the righthand value by the shell, just the
same as shell wildcards are expanded. So, for example, if I define the alias
alias rm='rm -i'
then when I type
$ rm foobar
the shell actually runs
$ rm -i foobar
which prompts me before doing anything rash like, oh, removing a file.
Aliases are also handy as a way of running short series of shell comamnds
that might otherwise go in a script. If they are short, consider implementing
such things as an alias rather than an script, saving load time. Most aliases
are better left to individual users, as people often rely on commands working
in the default way.
An example of how useful aliases can be is this short set to control the
cdplayer program on my system:
alias pa='cdplayer pause'
alias pl='cdplayer play $1'
alias re='cdplayer resume'
alias sk='cdplayer skip'
alias st='cdplayer stop'
alias vo='cdplayer volume $1'
Aliases are "inherited". That is to say, in:
alias vi='vi -F'
alias vp="vi $HOME/.profile"
The 'vi' in 'vp' will be expaned to 'vi -F'.
A useful way of seeing how much work I have to do is setup by the following:
alias la='ls -tr1 $HOME/articles/wip'
Another use of aliases is to save one remembering arcane syntax. why bother
with "chmod +x" or "chmod 0755" or whatever, when a two-character alias does
what you want i.e. makes a file exectuble?
alias cx="chmod +x $*"
Sometimes, one grows used to certain comamnds. On some flavours of Unix, for
example, the pager program is neither more nor less, but pg. Why bother
getting used to typing a new command. Simply use an alias.
alias pg='less'
Something I find useful is to have my browser point at a default location if I
don't give a URL on the command line.
alias lynx="lynx -cache 100 ${1:-$HOME/homepage/index.html}"
Functions
The shell is a programming langauge, remember, so it's only natural that it
should have functions. The syntax for functions is simple. They are useful
when aliases begin to grow unwieldy, and avoid the overhead of a shell script
at the cost of a bigger environment. Which is as good a place as any to
mention, that this stuff doesn't come free. The more stuff you do in your
init files -- setting variables, defining fucntions, etc -- the bigger your
environment -- an area of memory that each new process you start will copy
-- gets. For example, on my system
$ env | wc -c
3441
3,441 bytes. Quite a bit. You shouldn't notice preformance degradation on
a modern machine, but this is something to be aware of. If most of your
enivronment is only for interactive use, make sure that it isn't defined
for non-interactive shells.
I find a little set useful for simplying job control.
Simplifying Job Control
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}
}
So, b puts a job in the background, f brings it into the foreground again, and
k kills it -- all using handly job numbers rather than long-winded process
numbers. Finally, twep is a bit of whimsy.
Options
set -o notify
Notify the user when a background job completes.
set -o trackall
The shell "tracks" all aliased commands. That is, it "remembers" where
an executable file was found, and won't search PATH again for it if it is
still valid. (not bash)
set -o vi
Use vi commands for the command line editor. The other choice is emacs,
for Emacs keys.
set -o vi-tabcomplete
Allow the tab key to trigger file name completion (not in bash, which
has the feature by default anyway).)
ulimit -c unlimited
Ulimit is usually a shell built-in. It sets process limits, countrolling
such things as maximum file size, maximum size core dumps, whether core dumps
should be produced at all, etc. Limits set in a start-up file apply to your
login shell, and all processes it spawans. Here, I've simply set no limit to
core dump size (I've got plenty of space), and by implication taken the default
for other values. We can see the values currently in effect with ulimit -a:
time(cpu-seconds) unlimited
file(blocks) 2097152
coredump(blocks) unlimited
data(kbytes) unlimited
stack(kbytes) 8192
lockedmem(kbytes) unlimited
memory(kbytes) unlimited
nofiles(descriptors) 256
processes 256
# just because I'm paranoid, doesn't mean they're not after me...
umask 077
Always a good idea to set a umask, which describes the default permissions
to give to a new file. Here, any new file will have the normal permissions
for me, but the permission bits for group and other be "masked" (hence the
name) with octal 7, which has the effect of denying all permissions.
Start-up Commands
All sorts of thing can be run from the shell init file on start-up. This sort
of thing is best kept simple at a global level. Let individual users make
more fancy systems for themselves if they desire.
For example, the following would be suitable for /etc/profile:
# startup commands
echo;pom;echo;date
stty sane
This shows the current phase of the moon (pom is a little program on my
system; it isn't standard), and the date and time, with suitable spacing,
and makes sure that the terminal is in a usuable state (should always be,
but you never know, and it doesn't hurt to make sure).
/etc/profile is also a suitable place for fiddling with terminal
setttings. If you are supporting multiple users logging in from
various types of terminal, you can make sure everyone gets suitable
tty settings by doing the work in /etc/profile. This example is
more complicated, showing that we can use the full range of shell
programming constructs in our start-up files. Depending on what
line the user is coming in on, we may have to set some terminal
attributes -- no tab expansion for the Wyse 30, for example.
If the user is at the console, we give them some nice screen colours.
basetty=`basename \`tty\``
case $basetty in
ttyS6) export TERM=vt100 ;;
ttyS*) export TERM=wyse30; stty -tabs ;;
ttyp*) export TERM=vt100 ;;
# tty*) export TERM=linux80x30 ; /sbin/setterm -background
blue -foreground white; /sbin/setterm -store;;
tty*) export TERM=linux80x30 ; /sbin/setterm -background
white -foreground black; /sbin/setterm -store;;
esac
An individual user can run anything and everything from .profile. I always
play a sounds from a WAV file collection accumulated in too much idle time
on the Net:
#!/bin/sh
#random-sound.sh: play a random file from the sounds directory
count="`ls $HOME/sounds/*|wc -l|sed 's/ //'`"
count=`expr $count + 0`
export count
case $1 in
"b") soundfile=`echo sounds/*|awk 'BEGIN{srand()}
{x=1+int(rand()*number);print $x}' number=$count`;
echo $soundfile > /tmp/soundfile; 1>/dev/null 2>&1 wavplay $soundfile &;;
"p") soundfile=`echo sounds/*|awk 'BEGIN{srand()}
{x=1+int(rand()*number);print $x}' number=$count`;
echo $soundfile; 1>/dev/null 2>&1 wavplay $soundfile ;;
*) (1>/dev/null 2>&1 wavplay `echo sounds/*|awk 'BEGIN{srand()}
{x=1+int(rand()*number);print $x}' number=$count`) ;;
esac
I haven't the space to explain that, and besides this isn't the place; I
jsut show it as an example of the sort of thing you can do when you log in.
Remember, any command is valid here; though naturally, you'll prefer one
that terminates if you want to see your shell prompt anytime soon!
Working with the Command Line
Remember, there is no hard-and-fast division between the various files that
are sourced by your login shell, and what you can type at the command line.
Environment variables can be set interactively, functions defined, etc.
Althouhg I've taken the approach in this article of talking about the
aliases, functions etc as being defined in ~/.profile et al, that is only
because one normally finds it more convenient to store such things there,
rather than typing them in each time one logs on.
A word about command-line editing. Two types are commonly-available in
shells derived from the Korn shell. Those using the vi commands, and those
using emacs edting keystrokes. They are very different, and users who swear
by one will take pains to avoid the other. We've seen how toggle on the
one you prefer.
Resources
For pdksh, see
For bash, see
There are plenty of books on using the shell. The classic chapters in The
Unix Programming Environment are aging, but evergreen.
Another great resource that every shell user should have is
Unix Power Tools
from O'Reilly, now in its second edition.
Conclusion
The Unix shell, devised in the era of teletypes and 300-baud modems, is
still a useful tool in today's computing world. This article has shown you
how to get more out of that shell.
Author Biography
Paul Dunne is a writer and consultant. His at
home page
will tell you more about what he does.
Last Modified: April 12th, 1999
|