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.
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.
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.
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
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$
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}"
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 34413,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.
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.
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.
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;; esacAn 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!
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.
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.
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.