|
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 ( #!) 是告诉系统这个文件是由特定命令解释器解释的一组命令。 那个 #! 实际上是两个字节的 [1] 魔数, 魔数是指定文件类型的特殊记号,在此是表示这是一个可执行的shell脚本(键入 man magic可了解更多的信息)。紧跟着#!的是一个路径名.这个路径名是解释这个脚本内命令的命令解释器程序的路径:可能是一个shell,也可能是一个编程语言或者是一个软件包程序。这个命令解释器能执行脚本内的命令语句。它从脚本开头(即从#!所在行的下一行)起执行,但是忽略注释行。 [2]
1 #!/bin/sh
2 #!/bin/bash
3 #!/usr/bin/perl
4 #!/usr/bin/tcl
5 #!/bin/sed -f
6 #!/usr/awk -f
|
上面每一个脚本头行都是不同的命令解释器,如果第一行是/bin/sh, 那就是默认的Shell(Linux系统中bash是默认的shell),否则的话就是其他的解释器. [3] 如果使用#!/bin/sh/bin/sh(因为大多不同的商业UNIX都使用Bourne shell为默认shell)可以使脚本能够移植到非Linux的机器上,虽然这样做你将不能使用Bash许多特有的属性。但这样做的脚本遵循 POSIX [4] sh标准.
值得注意的是,在"#!"后面提供的路径必须是正确的,否则你运行脚本只会收到通常像"Command not found"那样的错误信息。
如果脚本程序只是由一组普通的系统命令而没有使用Shell内置命令的话#!将被忽略。上面的第二个例子被要求以#!,开头是因为变量赋值(lines=50),这就使用了Shell的特有的语句。再次提醒使用#!/bin/sh将调用默认的命令解释器,这在Linux系统上是/bin/bash.
 |
这份指南鼓励使用模块化的方法来写脚本。留意记录像“模板”的代码片断以备将来的脚本使用。最后你能生成一个很好的可扩展的例程库。下面的代码片断可以测试脚本是否被正确的数目参数调用。
1 E_WRONG_ARGS=65
2 script_parameters="-a -h -m -z"
3 # -a = all, -h = help, etc.
4
5 if [ $# -ne $Number_of_expected_args ]
6 then
7 echo "Usage: `basename $0` $script_parameters"
8 # `basename $0`是指脚本名称(译者:这个内容在后面章节会讲).
9 exit $E_WRONG_ARGS
10 fi
|
很多时候,你会写一个执行实际功能的脚本。本章的第一个脚本就是一个例子。以后它可能会使你记起把这个脚本扩展以完成类似的任务。使用变量代替固定的字符串常量是好的办法,像这样的办法还有用函数代替反复使用的代码块。
|
写完一个脚本,你能够运行它用命令:sh scriptname, [5] 另外也也可以用bash scriptname. 来执行(不推荐使用:sh <scriptname, 因为这样会禁止脚本从标准输入里读数据)。更为方便的是你可以使用chmod命令来使脚本自身变为可执行的.
-
你可以:
-
chmod 555 scriptname (使每个人都有读和执行的权限) [6]
-
也可以:
-
chmod +rx scriptname (使每个人都有读和执行的权限)
chmod u+rx scriptname (仅仅使脚本文件拥有者有读和执行的权限)
在给脚本加上执行权限之后,你可以很容易地使用./scriptname. [7] 来执行它。如果脚本以"#!"行开头,将会调用正确的命令解释器来执行它.
最后,把脚本测试并调试完后,如果想把脚本给系统中所有其他的用户使用,你应该把脚本移到目录/usr/local/bin 中(当然,这必须要有root的权限),这样只需简单地在命令行输入scriptname [回车]就能执行脚本了。