tcsh 是最流行的 UNIX® shell 之一。学习如何用 tcsh shell 变量简化自己的工作以及如何利用 tcsh 的高级安全特性。
tcsh 是原来的 Berkeley UNIX C shell 的改进版本,它是最流行的 UNIX shell 之一。本文讨论 tcsh 提供的一些功能:它提供的 shell 变量可以减少几个常规任务花费的时间,还提供了一些高级的安全特性,比如监视用户及其命令历史。本文描述的所有命令和脚本都用 tcsh 6.15 测试过(参见 参考资料)。
如何设置 shell 变量
tcsh 提供几个内置的 shell 变量。其中一部分(比如 rmstar 和 noclobber)是布尔值,所以建议用 set <variablename> 打开它们。对于 prompt 等其他内置变量,需要使用 set <variablename>=<value> 提供一个值。使用 unset <variablename> 取消变量。清单 1 给出一些基本示例。
清单 1. 如何设置/取消 shell 内置变量
tcsh# set prompt="arpan@tintin# " arpan@tintin# set autologout=1 arpan@tintin# unset prompt echo $autologout 1 <prompt has disappeared due to unset operation> |
下面几节讨论 tcsh 通过 shell 内置变量提供的一些最有用的特性。
用 rmstar 预防灾难
在 UNIX 中,造成混乱的最常见原因可能是意外地执行了 rm *。大多数用户在使用 rm 命令时不使用 -i 选项,因此会立即删除文件。tcsh 定义了一个 shell 变量 rmstar;如果打开这个变量,在用户执行操作时会显示提示,要求用户确认操作。但是,如果用户在命令提示下运行 rm –f *,就不会出现确认提示。清单 2 演示 rmstar 的用法。
清单 2. 使用 rmstar shell 变量
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# |
防止意外覆盖现有的文件
造成混乱的另一个典型场景是意外地覆盖现有的文件。为了防止发生这种情况,应该一直打开 shell 变量 noclobber。(这个变量在 csh shell 中也可用)。注意,这只能防止把输出重定向到现有文件;如果使用 cp 或 mv 覆盖文件,这个变量没有任何帮助。见清单 3。
清单 3. 使用 noclobber 防止意外覆盖文件
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 |
还要注意,shell 操作符 >> 和 >! 不考虑 noclobber 的设置。前一个操作符在现有文件中追加内容(所以仍然可以恢复数据),后者覆盖现有内容。
自动 Tab 补齐
当在 shell 提示上输入命令时,可以只输入命令字符串的一部分,然后按 Tab,shell 会自动补齐命令字符串或者给出可用的选择,这会显著加快输入速度。这个功能对于长文件名尤其有意义;可以只输入前几个字母并让 shell 补齐文件名。为了启用这个特性,需要设置 shell 变量 autolist。清单 4 给出一个示例。
清单 4. 使用 autolist 启用自动命令补齐
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 |
在这个示例中,[TAB] 表示按 Tab 键。在 shell 提示上输入 vi thi[TAB],shell 会把 thi[TAB] 扩展为 this_is_a_big_file。
使用 addsuffix 在 Tab 补齐期间区分目录
如果同时设置 addsuffix shell 变量和自动 Tab 补齐,那么在找到匹配时 tcsh 会在文件夹后面加上一个 / 字符,这样就更容易区分出文件夹。它在一般文件后面加一个空格。在清单 5 所示的情况中,有一个名为 documents 的文件夹,这个文件夹中有一个名为 deliverables 的文件;用户输入 do[TAB],shell 就会显示 documents/。如果取消 addsuffix 变量,tcsh 就只显示 documents,这对于判断 documents 是一般文件还是文件夹很不方便。
清单 5. 在使用 autolist 的同时使用 addsuffix 区分文件夹
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 |
使用 fignore shell 变量避免意外删除
在某些情况下,限制自动 Tab 补齐特性是有意义的。例如,如果 vi 是一个会话中最常用的命令,那么把 Tab 补齐提供的命令字符串限制在文本文件范围内可以节省时间。同样,如果 .c 和 .cpp 文件还未备份,希望避免意外删除它们,那么在 Tab 补齐期间最好不要出现具有 .c/.cpp 扩展名的文件,这样在 rm 命令后面使用 Tab 补齐特性时就不会删除它们。为了避免在 Tab 补齐期间显示 C/C++ 文件,使用 set fignore=(.c .cpp .h)。见清单 6。
清单 6. 使用 fignore 避免在 Tab 补齐期间显示源代码文件
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 |
注意,如果在 rm 后面按 Tab 键(而不是在 m 后面按 Tab 键),那么所有 C/C++ 源代码文件都会出现。
在没有用户活动时自动注销
数据安全是所有组织都很关心的问题。如果意外地让一个 shell 终端一直打开着,就可能提供访问重要文件的机会,这种情况随时都会发生。可以用 tcsh autologout 变量解决这个问题。如果在指定的时间(以分钟为单位)内没有用户活动,用户就从系统中注销,返回到 tcsh(如果 tcsh 是登录 shell)。如果 tcsh 不是登录 shell,用户就退出到以前的 shell(这对安全帮助不大)。因此,在安全环境中选择 tcsh 作为登录 shell 是有意义的。清单 7 给出一个由于没有用户活动导致自动注销的示例。
清单 7. 由于没有用户活动导致自动注销
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 |
改进 tcsh 的安全性:监视正在使用系统的每个用户
必须一直监视用户对受保护系统的访问。tcsh 提供了内置 shell 变量 watch,可以通过它查看正在使用系统的用户。语法是 set watch=(username1 ttyname1 username2 ttyname2 …)。这会监视用户 username1 是否在终端 ttyname1 上登录了。可以用特殊语法 set watch=(any any) 监视所有系统终端上的所有用户。
在默认情况下,watch 每 10 分钟检查一次系统中的登录/注销活动。可以通过 watch 语法中的第一个参数指定活动检查之间的时间间隔,例如:set watch=(5 any any)。见清单 8。
清单 8. 使用 watch 检查登录/注销活动
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 还提供内置命令 log,这个命令列出受 watch 变量影响的终端以及正在使用它们的用户(见清单 9)。注意,如果在没有设置 watch 的情况下使用 log,就会导致一个错误。
清单 9. 使用 log 检查 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 |
使用 prompt 变量跟踪当前的工作目录
tcsh 定义了 prompt 内置 shell 变量,可以通过这个变量定制 shell 提示。最常见的 UNIX 任务之一是跟踪当前所在的文件夹和机器。不需要一直使用 pwd 和 hostname,只需通过设置 prompt 变量让 shell 提示反映当前的工作目录和主机名,即可实现相同的效果。见清单 10。
清单 10. 通过修改 prompt 变量让 shell 提示反映当前的工作目录和主机
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] |
但是,这种方式有一个问题:如果切换到另一个文件夹,提示并不会反映这一变化。为了随着切换文件夹连续改变提示,应该使用特殊别名 cmdcwd。如果已设置这个别名,那么在切换到新文件夹之后 tcsh 会执行 cmdcwd 映射到的命令。为了在提示中反映修改后的文件夹,cmdcwd 必须映射到 set prompt 命令(见清单 11)。
清单 11. 使用 cmdcwd 别名在提示中反映修改后的文件夹
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] |
注意,这个方案不但能够反映 cd 命令所做的文件夹切换,对于 pushd 和 popd 命令也是有效的。如果使用 X-Windows,另一个跟踪当前文件夹的好方法是,在跨多个文件夹工作时,在 xterm 标题栏上显示文件夹名。
例如,可以使用 echo 命令在 xterm 标题栏上显示一些基本信息。在 shell 提示上输入 echo "[Ctrl-v][Esc]]2; Hello [Ctrl-v][Ctrl-g]"。注意,[Ctrl-v] 表示按组合键 Ctrl-V。输入这个命令序列,就会在 shell 提示上显示以下内容:echo "^[]2; Hello ^G"。执行这个命令之后,xterm 标题栏显示 Hello。清单 12 演示如何在 xterm 标题栏和提示中同时显示当前文件夹名。
清单 12. 使用 cmdcwd 修改提示并设置 xterm 标题栏
arpan@tintin[/home/arpan1/ibm1]# alias cwdcmd 'set prompt="$user@`hostname`[$cwd]# "; echo "^[]2;$cwd^G" ' |
自动纠正无效的命令用法
tcsh 提供内置变量 correct,这有助于纠正无效的命令用法。例如,如果希望调用 perl,但是输入的是 prl,tcsh 会提示您纠正它。清单 13 给出一个示例。
清单 13. 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 .. |
定期执行特定的命令
系统管理员最常见的任务之一是监视磁盘使用量,并在磁盘使用量接近 100% 时采取措施。tcsh 的一个出色特性使我们能够轻松地定期执行特定的命令。把 periodic 映射到要定期执行的任务,并把 shell 内置变量 tperiod 设置为执行任务的时间间隔(以分钟为单位)。清单 14 演示如何使用 tperiod 和 periodic。注意,periodic 映射到脚本 checkdiskusage,这个脚本检查磁盘使用量,tcsh 每 10 分钟运行它一次。
清单 14. 使用 tcsh 内置变量定期执行命令
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 |
针对每个终端分别设置历史文件
同一位 UNIX 系统用户常常从多个终端登录。为了针对每个终端分别维护命令执行历史,可以使用 histfile 和 savehist 环境变量。histfile 变量用来指定存储命令执行历史的文件;默认设置是 $HOME/.history。savehist 变量让 tcsh 存储用户在 shell 提示上输入的最后 N 个命令。清单 15 中定义的 histfile 变量指定多个历史文件,这样就可以监视多个终端。
清单 15. 使用 histfile 和 savehist 变量存储用户的命令历史
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 |
监视运行一个命令花费的时间
为了监视执行一个 UNIX 进程花费的时间,可以设置 time 变量。输出显示用户时间、内核时间和实际流逝时间。清单 16 给出一个示例。
注意,可以用 tcsh 的内置命令 time 产生相同的输出,但是要对脚本做大量修改 —— 每个命令必须加上前缀 time(例如,time du –sm /opt)。如果使用 time 变量,那么只需在脚本的开头加上一行 set time,就可以显示各个命令的执行时间。
清单 16. 使用 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 |
调试 shell 脚本:在发生错误时自动输出退出值
shell 变量 printexitvalue 是一个很有用的 tcsh 特性,它对脚本调试非常有帮助。在通常情况下,shell 脚本和 UNIX 程序在成功完成时返回零。如果设置这个变量,那么当脚本或程序返回非零值时 tcsh 会显示退出状态,这会指出潜在的错误。见清单 17。
清单 17. 使用 printexitvalue 帮助调试
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 |
注意,在这个变量与一个 shell 脚本结合使用时,显示的是脚本的非零返回值,而不是脚本内部使用的命令或用户程序的返回值。
结束语
除了支持 csh 提供的 shell 变量之外,tcsh 还提供了大量 shell 变量和别名。本文主要介绍 tcsh 特有的变量。这里只讨论了一部分变量;更详细的信息参见 参考资料。 (责任编辑:A6)