当前位置: 首页 > 文档资料 > Shell 中文文档 >

第二章:从一个 Sha-Bang(#!)开始

优质
小牛编辑
124浏览
2023-12-01

Shell 编程就像一个 1950 年代的自动点唱机… -- Larry Wall

在最简单的情况下,脚本程序不过是存储在一个文件里的系统命令列表。这至少让你执行它时不必重新按顺序键入相同功能的命令序列。


例子 2-1. cleanup: 一个清空/var/log目录下的日志文件的脚本

   1 # Cleanup
   2 # 必须以root用户运行.
   3
   4 cd /var/log
   5 cat /dev/null > messages
   6 cat /dev/null > wtmp
   7 echo "Logs cleaned up."

这没有什么不同寻常的,它不过是一组可以容易地从控制台或 xterm(译者注:一种图形虚拟控制台),中顺序键入的命令集。用一种脚本代替这组命令的用处是使你不必每次执行相同任务时都重复地顺序键入它们。脚本变成了一个工具,并且它也很容易地在一个实际项目被修改或者定制。


例子 2-2. cleanup: 一个改进版的 cleanup 脚本

   1 #!/bin/bash
2 # Bash脚本正确的头部.
3
4 # Cleanup, 版本 2
5 6 # 需要以root运行. 7 # 如果不是root用户,在此处添加错误信息打印代码和退出代码. 8 9 LOG_DIR=/var/log 10 # 使用变量比使用硬编码(hard-coded)更好。 11 cd $LOG_DIR 12 13 cat /dev/null > messages 14 cat /dev/null > wtmp 15 16 17 echo "Logs cleaned up." 18 19 exit # 这是从一个脚本中退出正确合适的方法

现在它看起来像一个真正的脚本了。但下面我们将做的更好…


例子 2-3. cleanup: 一个上面脚本的增强版,但不能处理错误

1 #!/bin/bash
2 # Cleanup, 版本 3
3
4 # 注意:
5 # -------
6 # 这个脚本使用了相当多的特性,这些我们稍后将会解释.
7 #
8 # 到那时,你已经学了这本书的一半了,你将不会再对shell感觉神秘了。
9 #
10
11
12
13 LOG_DIR=/var/log
14 ROOT_UID=0 # 只有用户ID变量$UID值为0的用户才有root权限.
15 LINES=50 # 默认的行数
16 E_XCD=66 # 不能进入到目录时的退出代码值
17 E_NOTROOT=67 # 不是root用户时退出的代码值
18
19
20 # 必须以root用户运行,以下进行检测
21 if [ "$UID" -ne "$ROOT_UID" ]
22 then
23 echo "Must be root to run this script."
24 exit $E_NOTROOT
25 fi
26
27 if [ -n "$1" ]
28 # 测试是否提供了命令行参数(即是测试命令行参数至少有一个参数)
29 then
30 lines=$1
31 else
32 lines=$LINES # Default, if not specified on command line.
33 fi
34
35
36 # Stephane Chazelas建议,
37 #+ 下面是一种更好的检测命令行参数的方法,
38 #+ 但是对于现在来说还是有些高级。
39 #
40 # E_WRONGARGS=65 # 不是数字参数 (参数格式不对)时的退出码
41 #
42 # case "$1" in
43 # "" ) lines=50;;
44 # *[!0-9]*) echo "Usage: `basename $0` file-to-cleanup"; exit $E_WRONGARGS;;
45 # * ) lines=$1;;
46 # esac
47 #
48 #* 可以跳到"循环"那章阅读开头一部分去了解上面的代码意思.
49
50
51 cd $LOG_DIR
52
53 if [ `pwd` != "$LOG_DIR" ] # 也可以用 if [ "$PWD" != "$LOG_DIR" ]
54 # 如果工作目录不在/var/log里?
55 then
56 echo "Can't change to $LOG_DIR."
57 exit $E_XCD
58 fi #在操作清空日志文件之前再次检查是否在正确的目录里
59
60 # 可以像下面再次确定是否在正确的目录里:
61 #
62 # cd /var/log || {
63 # echo "Cannot change to necessary directory." >&2
64 # exit $E_XCD;
65 # }
66
67
68
69
70 tail -$lines messages > mesg.temp # 保存message日志文件最后面几行日志信息到临时文件.
71 mv mesg.temp messages # 然后用临时文件覆盖messages日志文件
72
73
74 # cat /dev/null > messages
75 #* 上面这句把messages日志文件全部清空,这样没有上面那样保留最后几行安全
76
77 cat /dev/null > wtmp # ': > wtmp' and '> wtmp' have the same effect.
78 echo "Logs cleaned up."
79
80 exit 0
81 #
82 #一个脚本以0为退出代码表明脚本执行成功.

因为你可能并不希望把整个系统日志都清空,所以这个版本的cleanup保留了日志中最后的几行日志记录。如果你继续努力地学下去,将会发现更多精练的写法来代替上面的代码。

在脚本开头的

sha-bang(#!)

是告诉系统这个文件是由特定命令解释器解释的一组命令。

那个 #! 实际上是两个字节的可移植操作系统接口(Portable Operating System Interface),它尝试使类 UNIX 的操作系统标准化。POSIX 的规范可以在站点 Open Group site 找到。

[5] 注意:以 sh scriptname 运行一个Bash脚本将会禁止所有Bash的扩展特性。因此脚本可以会因此而运行失败。

[6] 因为运行一个脚本文件需要Shell能读此文件中的命令,因此不仅需要文件的执行权限,也需要文件有读的权限。

[7] 为什么不直接用简单地键入 scriptname 来运行脚本? 如果当前目录(由变量 $PWD )的值保存[译者注:也可由命令 pwd 打印当前目录路径])就是 scriptname脚本文件所在的目录,为什么直接键入scriptname不能运行脚本?这样运行脚本可能失败。因为由于安全的因素[译者注:如果你感兴趣的话,其中一个安全漏洞可在 LinuxSir 的 Shell 版中我发的一篇帖子里找到]当前目录默认不加入到用户的 $PATH,变量中。因此你必须明确地指定调用脚本的路径在当前路径./scriptname