![]() |
February 2002 | Search | Submit Article | Contact Us | Join Us | Merchandise |
7. How to create your own commands
7.1 Simple alias definitions
7.2 Advanced alias definitions
8. Miscellaneous topics
8.1 Environment variables
8.2 Shell variables
8.3 Shell programming
8.4 Programmed completion
8.5 Spelling correction
8.6 Command substitution
9. Making yourself at home
9.1 Shell execution modes
9.2 Start-up files
Part I published in the December 2001 issue of Daemon News gave a general introduction into history and tasks of Unix shells and dealt with command-line editing, command history, file name globbing and name completion in csh and tcsh.
Part II in the January 2002 issue discussed the following topics: Directory stack, input and output redirection, processes, jobs and job control.
Both, csh and tcsh, allow the definition of alias commands, which can be very convenient in various situations:
% alias ll ls -l % ll compile.log -rw-r--r-- 1 joe nobody 66 Dec 17 13:10 compile.log
When analyzing the second command line the shell recognizes that ll has been defined as an alias, and replaces this expression internally with ls -l. Then the ls command gets executed.
An example of the second category is the ping utility, which is located in the directory /sbin. This directory contains tools mostly useful for the system administrator only, so a normal user would have no reason to include it in the search path. However, ping can be used to see if a remote computer is alive:
% alias ping /sbin/ping % ping www.daemonnews.org PING www.daemonnews.org (204.152.186.46): 56 data bytes 64 bytes from 204.152.186.46: icmp_seq=0 ttl=46 time=208.675 ms 64 bytes from 204.152.186.46: icmp_seq=1 ttl=45 time=214.535 ms 64 bytes from 204.152.186.46: icmp_seq=2 ttl=46 time=195.677 ms ^C --- www.daemonnews.org ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max/stddev = 195.677/206.296/214.535/7.880 ms
The system queried answers correctly; the ping command is thus then terminated by pressing Control-C. The important thing here is that after ping is registered as an alias command using alias, it no longer has to be invoked with its full path name /sbin/ping.
If the alias command is entered without an argument, a list of all registered aliases is displayed:
% alias ll (ls -l) ping /sbin/ping
Some users prefer to make their working environment more fault tolerant:
% alias rm rm -i % rm compile.log remove compile.log? y
From now on, rm will always ask for a confirmation before removing files. To override the alias temporarily, one can enter:
% \rm compile.log
The file will be removed quietly since the \ character prevents the shell from alias substitution.
To permanently delete an alias, the unalias can be used:
% unalias rm
The alias command can also be invoked with only one argument; it then displays the corresponding alias definition:
% alias ll ls -l
7.2 Advanced alias definitions
The alias command ll defined in the previous section can be improved by adding a pipeline to the more utility, in order to stop output after every full screen. The solution seems to be simple:
% alias lm 'ls -l | more'
The quotes are necessary to prevent the shell from interpreting the pipeline symbol immediately, instead of waiting for a later execution of the alias command. The invocation of lm for the working directory then works flawlessly:
% lm total 18 -rwxr-xr-x 1 joe nobody 4241 Dec 18 11:14 a.out -rw-r--r-- 1 joe nobody 66 Dec 17 13:16 compile.err -rw-r--r-- 1 joe nobody 66 Dec 17 13:10 compile.log -rw-r--r-- 1 joe nobody 0 Dec 17 13:16 compile.out -rw-r--r-- 1 joe nobody 36 Dec 17 15:40 simple.c drwxr-xr-x 2 joe nobody 512 Feb 2 1996 tb
However, if one tries to use the command lm with an argument, e.g. the name of a directory, difficulties emerge:
% lm tb tb is a directory
What happens? The shell expands this command line to
% ls -l | more tb
which indeed makes no sense, since the argument gets placed in the wrong position, and more cannot display the contents of a directory. The correct command would be of course:
% ls -l tb | more
To solve the problem, the traditional access to the history buffer mentioned in section 2.3 of part I has to be used:
% alias lm 'ls -l \!* | more' % lm tb total 226 -rw-r--r-- 1 joe nobody 277 Nov 12 13:34 Makefile -rw-r--r-- 1 joe nobody 433 Nov 12 13:34 ascii.bas -rw-r--r-- 1 joe nobody 27928 Nov 12 13:34 bi.c -rw-r--r-- 1 joe nobody 3409 Nov 12 13:34 ci.c -rw-r--r-- 1 joe nobody 1835 Nov 12 13:34 help.c -rw-r--r-- 1 joe nobody 1633 Nov 12 13:34 io.c -rw-r--r-- 1 joe nobody 838 Nov 12 13:34 tb.c -rw-r--r-- 1 joe nobody 3986 Nov 12 13:34 tb.h -rw-r--r-- 1 joe nobody 48593 Nov 12 13:34 tb.ps -rw-r--r-- 1 joe nobody 16270 Nov 12 13:34 tb.tex -rw-r--r-- 1 joe nobody 6013 Nov 12 13:34 utils.c
The backslash character blocks the interpretation of !* at the time of alias definition. Later, when the alias command is invoked, this character string is replaced by the list of arguments actually entered. When evaluating alias commands containing history event specifications, the shell considers the command entered by the user as the last command.
Mostly all history event specifications can be used within alias definitions. For example,
% alias bak 'cp -p \!:1 \!:1.bak'
creates an alias command bak which makes a backup copy of the file specified as first argument:
% bak simple.c % ls -l simple.c* -rw-r--r-- 1 joe nobody 36 Dec 17 15:40 simple.c -rw-r--r-- 1 joe nobody 36 Dec 17 15:40 simple.c.bak
A set of variables forms the environment of a process. These variables provide some information which may be useful for processes. The shell allows to list all environment variables as well as to change them or to add new ones. The complete environment of a parent process is passed to a child process on invocation. This means, each program started by the shell on a user's request, inherits a copy of the set of environment variables the shell owns. The command printenv gives a list of all environment variables:
% printenv PATH=/bin:/usr/bin:/usr/X11R6/bin:/usr/local/bin:/usr/games:. TERM=xterm MAIL=/var/mail/joe USER=joe HOME=/usr/home/joe SHELL=/bin/tcsh HOSTTYPE=FreeBSD VENDOR=intel OSTYPE=FreeBSD MACHTYPE=i386 SHLVL=1 PWD=/usr/home/joe LOGNAME=joe GROUP=nobody HOST=hal9000
Worthwhile to remember are especially the variables PATH, the value of which defines the search path for executable files in the system, and SHELL containing the name of the shell to invoke whenever a utility program needs to start a shell.
To add further environment variables, the setenv command can be used:
% setenv EDITOR emacs
This variable tells all utilities which editor to start if necessary. Environment variables can be removed by using unsetenv:
% unsetenv EDITOR
To read out the value of an environment variable, use a leading dollar sign as for usual shell variables:
% echo $USER joe
Shell variables have already been introduced in section 2.1. The shell allows the use of variables which can store data. These variables are different from environment variables, they are local to the shell and not passed to child processes. Various shell variables with pre-defined meanings have already been mentioned in many sections of this article series. The informational content of a variable can lie in whether it is set or not or in its value.
Variable names must consist of letters, digits, or the underscore character; the leading character has to be a letter, and the name may not be longer than twenty characters. Variables can be of scalar or array type and may take numerical or string values:
% set three = 3 % set vowels = ( a e i o u ) % echo $three 3 % echo $vowels[2] e
It is possible to test whether a shell (or environment) variable has been set or not by using the prefix $?:
% echo $?three 1 % echo $?two 0
As can be seen from the example, if the variable is not set, the operation will return zero.
The values of some special shell and environment variables are kept identical by the shell. The variables in question are group and GROUP, home and HOME, path and PATH, shlvl and SHLVL, term and TERM and user and USER.
Some useful shell variables which are not mentioned anywhere else in the article series are:
Both, csh and tcsh, offer a set of internal (built-in) commands which allow to write shell scripts looking (in a way) similar to C code. That is the reason why csh is called C shell. By the way, the T in tcsh is derived from the name of the TENEX operating system the user interface of which already provided useful command completion about 25 years ago. This impressed and influenced the author of tcsh.
A famous article csh programming considered harmful has been posted to some newsgroups years ago, and indeed, csh has had a lot of bugs and disadvantages.
In the meantime, bugs have been fixed, and tcsh behaves much better. Nevertheless, both shells should at most be used for small scripts only. The reason simply is that even tcsh is weak in advanced i/o redirection which is often needed in shell programming.
However, interactive use is a different matter, and especially tcsh is quite strong here. So, this article has a focus on interactive use.
One instruction useful not only in scripts but often also when entering commands interactively is foreach to process a sequence of arguments; e.g., to print some manual pages on a PostScript printer:
% foreach file ( /usr/share/man/man1/{csh,gcc,tcsh}.1.gz ) foreach? echo $file foreach? gunzip < $file | groff -man | lpr -Pps foreach? end /usr/share/man/man1/csh.1.gz /usr/share/man/man1/gcc.1.gz /usr/share/man/man1/tcsh.1.gz
Name completion in csh and tcsh has been introduced in section 3.2. Programmed name completion is an additional feature tcsh offers and should not be confused with script programming.
Two simple examples will illustrate programmed completion. When trying to complete an argument of the commands cd or rmdir, the shell will by default offer plain files as possible completions, too. To change this, enter:
% complete cd 'p/1/d/' % complete rmdir 'p/*/d/'
From now on, for cd and rmdir, when completing a word in a given position (letter p), complete the first word (digit 1) or all words (character *) with a directory name (letter d) only.
The second example will modify the behavior of the shell when requesting completion while starting ftp sessions:
% complete ftp 'p/1/(ftp.freebsd.org ftp.netbsd.org ftp.openbsd.org)/' % ftp ftp.f<TAB>reebsd.org
Another additional feature of tcsh is spelling correction, a convenient way to handle typos. After assigning one of the values cmd or all to the shell variable correct, tcsh will offer some aid:
% set correct = cmd % moer simple.c CORRECT>more simple.c (y|n|e|a)? yes #includemain(){for(;;);}
As shown in the example, the correction can be accepted (y), or otherwise rejected (n), or the command can be edited (e) or aborted (a).
After setting correct to all, tcsh will
offer correction not only for command names, but for arguments,
too. This can be nerving and dangerous if entered commands create
new files since the correction mechanism will treat names of
non-existing files as typos.
8.6 Command substitution
Command substitution is a way to use data written by utility programs to the standard output channel directly in the shell command line. The date utility may serve as an example; it can be used to print the current time to stdout:
% date '+%H:%M:%S' 11:17:58
Now, one can use this kind of output to construct file names:
% cc simple.c >& compile.log.`date '+%H:%M:%S'` % ls -l compile.log.* -rw-r--r-- 1 joe nobody 0 Jan 15 11:23 compile.log.11:23:11
Any expression enclosed in backquotes is regarded as a command, executed, and replaced by the resulting output (newlines are replaced by spaces). It is possible to use history event specifications within backquotes.
Three modes of execution of a shell have to be distinguished:
Common (system-wide) shell start-up files can be used to set up a convenient environment for all users on the system, and personal start-up files allow to create an individual working environment. Furthermore, there are «shutdown» files too, getting executed during the process of logging out. Among others, typical tasks of start-up files are to set environment and/or shell variables and to define alias commands.
File name | Use |
---|---|
/etc/csh.cshrc | Common start-up file read by all shells |
/etc/csh.login | Common start-up file read by login shells |
/etc/csh.logout | Common shutdown file read by login shells |
~/.cshrc | Personal start-up file read by all shells (csh) |
~/.tcshrc | Personal start-up file read by all shells (tcsh) |
~/.login | Personal start-up file read by login shells |
~/.logout | Personal shutdown file read by login shells |
On invocation of a new shell, the common start-up files are read first. Thus a user can override the system-wide settings by his or her personal ones since the personal files are read subsequently. Login shells read /etc/csh.login after /etc/csh.cshrc and ~/.login after ~/.cshrc. If tcsh does not find ~/.tcshrc, it will then read ~/.cshrc. Furthermore, tcsh can be compiled in such a way that the two login files are read prior to the cshrc files (the version shell variable will then contain the option string lf).
Commands that need to be executed only once during a session should be placed in one of the login files. Typical examples are commands that affect environment variables. Instructions setting shell variables or defining aliases have to be included in the cshrc files.
By the way, don't get confused by the file /.cshrc in the root directory/. It is read by single-user shells in single-user mode only. In normal multi-user operation of the system, the file is disregarded. Modifications to /.cshrc require a lot of care since you may lock yourself out completely in single-user mode!
In general, after changing one of the start-up files, do not log out before you have been able to log in successfully on a different virtual or pseudo-terminal or in a different window. This is the best way to test and to repair the files if necessary.
Non-interactive shells read the cshrc files, too; but this can be suppressed by the command line option -f on invocation.
Here is an example for /etc/csh.login or ~/.login which picks up topics covered in the article series; the character # can be used to comment within these files:
# /etc/csh.login or ~/.login # set search path for executable files set path = ( /bin /usr/bin /usr/X11R6/bin /usr/local/bin /usr/games . ) if ( "$user" == root ) set path = ( /sbin /usr/sbin /usr/local/sbin $path ) # set the default editor setenv EDITOR emacs # background jobs will be suspended if they try to write to stdout/stderr stty tostop # print a random adage fortune # allow written messages from other users mesg y # everyone can read and execute newly created files umask 022
The next example shows a possible file /etc/csh.cshrc or ~/.cshrc:
# /etc/csh.cshrc or ~/.cshrc # settings will be done only for interactive shells if ( $?prompt ) then # set useful csh variables set filec set ignoreeof set history = 250 set noclobber set prompt = "${user}@`hostname -s`% " set savehist # set useful additional or modified tcsh variables if ( "$shell:t" == tcsh ) then set autolist set correct = cmd set prompt = '%n@%m[%~]%% ' set rmstar set savehist = ( 250 merge ) endif # set useful csh aliases alias ping /sbin/ping alias pd pushd alias lf ls -F alias ll ls -l alias lm 'ls -l \!* | more' # set useful tcsh aliases and programmed completions if ( "$shell:t" == tcsh ) then alias lf ls-F # tcsh built-in with faster execution complete {cd,pd} 'p/1/d/' complete rmdir 'p/*/d/' endif # set different root shell prompt if ( "$user" == root ) set prompt = "`hostname -s`# " endif
Now we are at the end of this article series. Not all features of csh and tcsh could be mentioned in depth. But what did we learn? It was my intention to show that csh and especially tcsh are absolutely useful when interactively used by system administrators and ordinary users. Linux was soon tied down to bash since this is the GNU shell. But BSD systems come with a true Bourne shell able to run scripts and - with csh or tcsh. Use bash if you like - but it is not a law of nature to do so on a BSD system!
[1] tcsh man page; command: man tcsh
[2] Paul DuBois: Using csh&tcsh, O'Reilly 1995, ISBN 1-56592-132-1