Tuesday, January 15, 2008

Tcsh shell variables

Tcsh is one of the most popular UNIX® shells. Learn how you can use tcsh shell variables to make your work easier and how to take advantage of tcsh's advanced security features.

Tcsh, an enhancement of the original Berkeley UNIX C shell, is one of the most popular UNIX shells. This article looks at some of the power that tcsh brings to the table: it provides shell variables that make several regular tasks less time consuming, and it also brings in advanced security features like monitoring of users and their command histories. All commands and scripts described in this article have been tested with tcsh 6.15 (see the Resources section).

How to set shell variables

Tcsh comes with several built-in shell variables. Some of these, like rmstar and noclobber, are boolean in nature, so I recommend that you use set <variablename> to turn them on. For other built-ins like prompt, you need to provide a value using set <variablename>=<value>. To unset a variable, use unset <variablename>. Listing 1 shows some basic examples.


Listing 1. How to set/unset shell built-in variables
 
tcsh# set prompt="arpan@tintin# "
arpan@tintin# set autologout=1
arpan@tintin# unset prompt
 echo $autologout
1
 <prompt has disappeared due to unset operation>

 

The next few sections describe some of the most useful features tcsh provides through shell built-ins.

Prevent disaster with rm

Perhaps the most common way of messing things up in UNIX is to inadvertently issue rm *. Most users don't use the -i option with rm, thereby deleting the files instantly. Tcsh defines a shell variable rmstar; when turned on, it provides the user with a prompt that requests confirmation of the user's action. However, it doesn't work if the user runs rm –f * at the command prompt. Listing 2 shows the use of rmstar.


Listing 2. Using the rmstar shell variable
 
arpan@tintin# pwd
/home/arpan/scratchpad
arpan@tintin# ls
file1 file2
arpan@tintin# set rmstar
arpan@tintin# rm *
Do you really want to delete all files? [n/y] n
arpan@tintin# ls
file1 file2
arpan@tintin# unset rmstar
arpan@tintin# rm * 
arpan@tintin# ls
arpan@tintin# 

 

Prevent accidental overwriting of existing files

Another typical doomsday scenario is the accidental overwriting of an existing file. To prevent this from happening, always keep the noclobber shell variable turned on. (This variable is also available in the csh shell.) Note that this can only save you while you're trying to redirect output to an existing file -- it isn't any help if you're using cp or mv to overwrite the file. See Listing 3.


Listing 3. Using noclobber to prevent accidental file overwrite
 
arpan@tintin# ls 
file1 file2
arpan@tintin# set noclobber
arpan@tintin# echo testing > file1
file1: File exists.
arpan@tintin# unset noclobber
arpan@tintin# echo testing > file1
arpan@tintin# cat file1
testing

 

Also note that the shell operators >> and >! override noclobber. The former operator appends to the existing file (so data may still be recovered), and the latter overwrites existing content.

Automatic Tab completion

When you're typing commands at the shell prompt, it speeds things up considerably if you can type in part of the command string, click Tab, and have the shell either complete the command string or provide options for completion. This functionality is particularly useful with long filenames -- you can key in the first few letters and let the shell complete the filename. To enable this feature, you need to set the autolist shell variable. Listing 4 gives an example.


Listing 4. Using autolist for automatic command completion
 
arpan@tintin# ls
this_is_a_big_file test.c threads.h
arpan@tintin# set autolist
arpan@tintin# vi t[TAB]
this_is_a_big_file test.c term.h
arpan@tintin# vi th[TAB]
this_is_a_big_file threads.h

 

In this example, [TAB] indicates clicking the Tab key. Type in vi thi[TAB] at the shell prompt, and the shell expands thi[TAB] to this_is_a_big_file.

Use addsuffix to distinguish directories during Tab completion

If the addsuffix shell variable is set along with automatic Tab completion, it's easier to distinguish folders because tcsh suffixes them with a / character when a match is found. It suffixes normal files with a space. Listing 5 shows a situation with a folder named documents and a file named deliverables in the same folder; the user types in do[TAB], and in response the shell displays documents/. If the addsuffix variable is unset, then tcsh displays only documents, which is inconvenient because you need to determine whether documents is a normal file or a folder.


Listing 5. Using addsuffix for additional clarity while using autolist
 
arpan@tintin# ls
documents deliverables
arpan@tintin# set autolist
arpan@tintin# ls do[TAB]
arpan@tintin# ls documents
arpan@tintin# set addsuffix
arpan@tintin# ls do[TAB]
arpan@tintin# ls documents/
arpan@tintin# unset autosuffix
arpan@tintin# ls do[TAB]
arpan@tintin# ls documents

 

Use the fignore shell variable to avoid accidental deletion

It makes sense to restrict the automatic Tab-completion feature under certain circumstances. For example, if vi is the most-used command in a session, then you can save time if only text files come up during Tab completion. Likewise, if .c and .cpp files aren't yet backed up and you want to avoid accidental deletion at all cost, then it's best to avoid files with .c/.cpp extensions during Tab completion so they aren't deleted when you use rm followed by Tab completion. To prevent C/C++ file types from showing up during Tab completion, use set fignore=(.c .cpp .h). See Listing 6.


Listing 6. Using fignore to prevent source files from showing up during Tab completion
 
arpan@tintin# set autolist
arpan@tintin# ls
memory.h memory.cpp kernel.c memory.o kernel.o
arpan@tintin# rm m[TAB]
memory.h memory.cpp memory.o
arpan@tintin# set fignore=(.c .cpp .h)
arpan@tintin# rm m[TAB]
memory.o

 

Note that if you use rm followed by Tab, as opposed to m followed by Tab, then all C/C++ source files appear.

Automatically log out in case of no user activity

Data security is a prime concern in all organizations. Leaving a shell terminal open inadvertently can potentially provide access to secure files, but this happens all the time. You can solve this problem using the tcsh autologout variable. When there is no user activity for a specific time (measured in minutes), the user is logged off the system to tcsh (if it's the login shell). If tcsh isn't the login shell, the user exits to the previous shell, which isn't much help. Thus, it makes sense to have tcsh as the login shell of choice in a secure environment. Listing 7 shows a case of automatic logout due to inactivity.


Listing 7. Automatic logout in case of inactivity
 
arpan@tintin# rsh herge
arpan@herge# set autologout=1
arpan@herge# date
Sat Jun 28 18:13:07 IST 2008

<After 1 min of inactivity>

arpan@herge# auto-logout
Connection to herge closed. 
arpan@tintin# date
Sat Jun 28 18:14:10 IST 2008

 

Enhanced security in tcsh: Monitor everyone who's using the system

Access to a restricted system must be continuously monitored. Tcsh provides the built-in shell variable watch, which makes it easy to view who is logging in and out of the system. The syntax is set watch=(username1 ttyname1 username2 ttyname2 …). This monitors whether the user with username1 is logged on to terminal ttyname1. The special syntax set watch=(any any) lets you monitor all users across all system terminals.

By default, watch checks the system for login/logout activity every 10 minutes. You can override this behavior by specifying the time between activity checks as the first variable of the watch syntax: for example, set watch=(5 any any). See Listing 8.


Listing 8. Using watch to check for login/logout activity
 
arpan@tintin# set watch=(5 any any)
<checks for login/logout activity across system every 5 minutes)
arpan@tintin# set watch=(b* any)
<check the login/logout activities of all users whose name starts with b across 
    any terminal in the network>

 

Tcsh provides the built-in command log, which lists the terminals affected by watch variables and who's using them (see Listing 9). Note that using log without watch being set causes an error.


Listing 9. Using log to check for terminal usage under watch
 
arpan@tintin# log
arpan has logged on pts/0 from 132.132.6.73
root has logged on console
zanies has logged pts/5 from 132.132.2.1

 

Use the prompt variable to keep track of the current working directory

Tcsh defines the prompt built-in shell variable, which you can use to customize shell prompts. One of the most common UNIX tasks is keeping track of which folder and machine you're currently in. Instead of continuously using pwd and hostname, you can achieve the same effect by manipulating the prompt variable to reflect the current working directory and hostname. See Listing 10.


Listing 10. Change the prompt variable to reflect the current working directory and host
 
tcsh-6.15$ pwd
/home/arpan/ibm1
tcsh-6.15$ hostname
tintin
tcsh-6.15$ echo $user
arpan
tcsh-6.15$ set prompt="$user@`hostname`[$cwd] "
arpan@tintin[/home/arpan/ibm1] 

 

But an issue remains with this approach: if you change to a different folder, the prompt doesn't reflect the change. To make this change continual as you switch folders, you use the special alias cmdcwd. If this alias is set, then tcsh executes the command that cmdcwd was aliased to after switching to a new folder. To reflect the changed folder in the prompt, cmdcwd must be aliased to the set prompt command (see Listing 11).


Listing 11. Use the cmdcwd alias to reflect changed folders in the prompt
 
tcsh-6.15$ alias cmdcwd 'set prompt="$user@`hostname`[$cwd] " '
tcsh-6.15$ cd 
arpan@tintin[/home/arpan/ibm1] cd net
arpan@tintin[/home/arpan/ibm1/net] 

 

Note that this scheme works seamlessly with the pushd and popd commands as well, not just with cd while changing folders. If you're using X-Windows, yet another smart way to keep track of the current folder is to display the folder name on the title bar of the xterm as you work your way across multiple folders.

For example, you can print some basic information on the xterm title bar using the echo command. Type echo "[Ctrl-v][Esc]]2; Hello [Ctrl-v][Ctrl-g]" at the shell prompt. Note that [Ctrl-v] means to click the key combination Ctrl-V. Keying in this sequence displays the following at the shell prompt: echo "^[]2; Hello ^G". On execution of this command, the xterm title bar displays Hello. Listing 12 shows how to display the current folder name in the xterm title bar along with the prompt.


Listing 12. Use cmdcwd to change the prompt and set xterm title bar
 
arpan@tintin[/home/arpan1/ibm1]# alias cwdcmd 'set prompt="$user@`hostname`[$cwd]# "; 
    echo "^[]2;$cwd^G" '

 

Automatically correct invalid command usage

Tcsh provides the built-in variable correct, which helps you correct invalid command usage. For example, if you want to invoke perl and have typed in prl, tcsh lets you correct it. Listing 13 gives an example.


Listing 13. Automatically correct typos with tcsh
 
arpan@tintin# set correct=cmd
arpan@tintin# prl 
CORRECT>perl (y|n|e|a)? y
..
arpan@tintin# figner
CORRECT>finger (y|n|e|a)? y
..

 

Periodic execution of specific commands

One of the most common tasks system administrators need to do is monitor disk usage and take action if it nears 100%. Tcsh has a great feature that lets you perform such periodic execution of events with ease. Alias periodic to the task you want to be executed on a periodic basis, and set the shell built-in tperiod equal to the number of minutes between executions of the task. Listing 14 shows how to use tperiod and periodic. Note that periodic is aliased to the script checkdiskusage, which checks for disk usage and is run by tcsh every 10 minutes.


Listing 14. Use tcsh built-ins for periodic execution of commands
 
arpan@tintin# set tperiod=10
arpan@tintin# alias periodic checkdiskusage
arpan@tintin# cat checkdiskusage
df -k | awk -F" " '{print $5}' | grep "9[0-9]*"
if ($status <> 0) then 
  mail –s "disk quota exceeded 90%" root@officemail.com
endif
exit $status

 

Set a history file on a per-terminal basis

It's fairly common to have the same user of a UNIX system logged on from multiple terminals. To maintain the command execution history on a per-terminal basis, you can use the histfile and savehist environment variables. The histfile variable lets the user specify the name of the file where the command execution history should be stored; the default is $HOME/.history. The savehist variable asks tcsh to store the user's last N commands on the shell prompt. The histfile variable definition in Listing 15 allows for multiple history files so you can monitor multiple terminals.


Listing 15. Use the histfile and savehist variables to store the user's command history
 
arpan@tintin# tty
/dev/pts/0
arpan@tintin# set savehist=25
arpan@tintin# set histfile=~/.history_`tty | sed –e 's/\//_/g' `
arpan@tintin# echo $histfile
~/.history_dev_pts_0

 

Monitor the time it takes to run a command

To monitor the time it takes for a UNIX process to execute, you can set the time variable. The output displays the user time, kernel time, and real elapsed time. Listing 16 shows an example.

Note that you can achieve the same output using tcsh's built-in time command, but the script change isn't minor -- every command must be prefixed with time (for example, time du –sm /opt). If you use the time variable, the single line set time at the start of the script is good enough to display the time it takes to run individual commands.


Listing 16. Use time to display individual commands' execution time
 
arpan@tintin# cat script
set time
du –sm /opt
df –k /lib
arpan@tintin# tcsh –f ./script
198  /opt
0.628u 0.008s 0:02.00 0.0%      0+0k 0+0io 0pf+0w
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/sda1             15773312     1125772  13846300   8% /
0.000u 0.004s 0:00.02 0.0%      0+0k 0+0io 0pf+0w

 

Debug shell scripts: Automatically print the exit value on error

The shell variable printexitvalue is a useful feature of tcsh that immensely aids script debugging. Typically, shell scripts and UNIX programs return 0 on successful completion. If you set this variable, tcsh displays the exit status whenever a script or program returns a non-zero value, thereby indicating a potential error. See Listing 17.


Listing 17. Use printexitvalue for debugging
 
arpan@tintin# set printexitvalue
arpan@tintin# ls /tmp/opt
ls: /tmp/opt: No such file or directory
Exit 2
arpan@tintin# cat error_script
ls –l; return 2
arpan@tintin# ./error_script
./error_script: line 1: return: can only `return' from a function or sourced script
Exit 1
arpan@tintin# unset printexitvalue; ls /tmp/opt
ls: /tmp/opt: No such file or directory

 

Note that when you use this variable in conjunction with a shell script, the non-zero return value of the script is displayed, not the individual return values of commands or user programs that the script may use internally.

Conclusion

Tcsh provides a gamut of shell variables and aliases, in addition to supporting those that csh supplies. This article has focused mainly on the variables that are unique to tcsh. The discussion covers only a cross section of the variables; for a detailed discussion, see the Resources section.

No comments: