《UNIX/Linux 系统管理技术手册(第四版)》——2.2 bash脚本编程

2.2 bash脚本编程

UNIX/Linux 系统管理技术手册(第四版)
bash特别适合编写简单的脚本,用来自动执行那些以往在命令行输入的操作。在命令行用的技巧也能用在bash的脚本里,反之亦然,这让用户在bash上投入的学习时间获得了最大的回报。不过,一旦bash脚本超过了100行,或者需要的特性bash没有,那么就要换到Perl或者Python上了。

bash脚本的注释以一个井号(#)开头,并且注释一直延续到行尾。和命令行中一样,可以把逻辑上的一行分成多个物理上的多行来写,每行末尾用反斜线消除换行符(newline)。还可以用分号分隔语句的办法,在一行里书写多条语句。

bash脚本可以只包含一系列的命令行,此外其他什么都没有。例如,下面的helloworld脚本就只有一条echo命令。

!/bin/bash

echo "Hello,  world!"

第一行叫做“#!”语句,它声明这个文本文件是一个脚本,要由/bin/bash来解释。内核在决定如何执行这个文件的时候,要先找这个语句。从派生出来执行这个脚本的shell的角度来看,“#!”行只是一个注释行。如果bash不在这行指定的位置那里,那么就需要调整这行的内容。

要让这个文件做好能运行的准备,只要设置它的可执行位即可(参考6.5.5节)。

$ chmod +x  helloworld

$ ./helloworld3

Hello, world!

1

还可以把shell当做解释程序直接调用:

$ bash helloworld

Hello, world!

$ source helloworld

Hello, world!

第一条命令在一个shell的新实例中运行helloworld脚本,第二条命令让当前的登录shell读取并执行这个文件的内容。当这个脚本用来设置环境变量,或者只对当前的shell做定制的时候,就采用后一种选择。在脚本编程中,这种形式常用来加入一个配置文件的内容,该文件里面写的是对一系列bash变量进行赋值2。

如果是Windows用户,那么可能已经习惯于这样的做法,即由文件的扩展名标明该文件的类型,以及是否可以执行。但在UNIX和Linux上,要由文件的权限位来指定一个文件是否可以执行,如果可执行,那么由谁可以执行。如果愿意,可以给自己的bash脚本加.sh后缀,提醒用户它们是什么文件,但在运行该命令的时候,就必须得输入.sh,因为UNIX不会对扩展名做特殊处理。参考6.5.1节了解有关权限位的更多知识。

2.2.1 从命令到脚本
在我们开始介绍bash的脚本编程特性之前,先讲一下方法。大多数人写bash脚本的时候,都按照和他们写Perl或者Python脚本一样的方式:用一个文本编辑器来写。不过,把常规的shell命令行当做一种交互式的脚本开发环境,考虑这样用的话,效果会更高。

例如,假定在一个目录层次结构中,散布着很多日志文件,它们的名字后缀为.log和.LOG,现在想把它们都改为大写的形式。首先,让我们看看是否能找到所有这样的文件。

$ find  . -name ' *log '

.do-not-touch/important.log admin.com-log/

foo.log genius/spew.log leather_flog

…

哦,看起来我们要在搜索模式中包括点号(.),而且还要排除目录。键入重新找回这条find命令,然后对它进行修改。

$ find  . -type f -name ' *.log '

.do-not-touch/important.log foo.log

genius/spew.log

…

好了,这次看上去结果更好了。不过,.do-not-touch目录看上去挺危险的;我们或许不应该让它出来捣乱。

$ find  . -type f -name ' *.log ' | grep  -v  .do-not-touch

foo.log genius/spew.log

…

好了,正好剩下需要的文件清单。让我们生成一些新的名字。

$ find  . -type f -name ' *.log ' | grep  -v  .do-not-touch | while read  fname

> do

> echo mv  $fname ${fname/.log/.LOG/}

> done

mv  foo.log foo.LOG

mv  genius/spew.log genius/spew.LOG

…

好,那几条命令就是我们想要的命令,把它们运行起来就可以执行改名操作。那么在现实中,我们该怎么做呢?我们可以把这条命令重新找回来,编辑一下把echo去掉,让bash执行mv命令,而不仅仅是打印mv命令。不过,用管道把这些命令都送到另一个shell的实例,这样更不容易出错,而且需要对前面命令做的编辑也更少。

当键入的时候,我们会发现bash考虑得很精心,它把这个小小的脚本变成了一行。对于这个紧凑的命令行,我们只要加一个管道,把输出送给bash -x就行了。

$ find  . -type f -name ' *.log ' | grep  -v  .do-not-touch | while read  fname; do echo mv  $fname  ${fname/.log/.LOG/}; done  | bash -x

+ mv  foo.log foo.LOG

+ mv  genius/spew.log genius/spew.LOG

…

给bash加了-x选项后,它在执行每条命令之前,会先打印这条命令。

我们现在已经完成了实际的改名工作,但是仍然想把整个脚本保存下来,以便可以再次使用它。bash的内置命令fc和非常像,但它不是让上次的命令重新出现在命令行,而是把该命令送到用户选择的编辑器里。再加一个“#!”行和用法说明之后,把这个文件写到一个可以执行的地方(或许是~/bin,或者/usr/local/bin),让这个文件可执行,于是就得到了最终的脚本。

上述方法总结如下:

按一个管道的方式开发脚本(或者脚本的组成部分),一次开发一步,完全都在命令行上做;
把输出送到标准输出,检查并确保结果正确;
每开发一步,用shell的history命令重新找回命令管道,用shell的编辑功能调整它们;
在得到正确输出之前,都不实际执行任何操作,所以如果命令不正确,也不需要撤销什么操作;
一旦得到正确的输出,就真正执行命令,并核对命令能按预期要求工作;
用fc命令捕获工作结果,整理后保存下来。
在上面的例子里,我们打印出数行命令,然后用管道把它们送入一个子shell去执行。这一技术并不一定行得通,但它经常还是有帮助的。另一种做法是,可以把输出重定向到一个文件,得到这个结果。无论怎样,都要预先看到正确的结果,才做任何可能有破坏性的操作。

2.2.2 输入和输出
echo命令虽然原始,但易于使用。要想对输出做更多的控制,就需要使用printf命令。因为采用printf的话,必须显式地在必要的地方加换行符(用“\n”),所以它用起来稍有不便,不过它也能让用户使用制表符,而且能让输出里的数字有更好的格式。比较下面两条命令的输出。

$ echo "\taa\tbb\tcc\n"

\taa\tbb\tcc\n

$ printf  "\taa\tbb\tcc\n"

aa  bb  cc

有些系统带有操作系统级的echo和printf命令,通常分别位于/bin和/usr/bin目录下。虽然这两条命令和shell的内置命令都很相似,但是它们的细节还是稍有不同,特别是printf,差别更大一些。对此,要么坚持采用bash的语法,要么用完整路径名调用外部的printf命令。

用read命令可以提示输入。下面是一个例子:

!/bin/bash

echo -n  "Enter  your name:  " read user_name

if [ -n  "$user_name" ]; then echo "Hello  $user_name!" exit  0

 else

fi

 echo  "You did  not tell  me  your name!" exit  1

echo命令的-n选项消除了通常的换行符,但也可以在这里用printf命令。我们简要介绍一下if语句的语法,它的作用在这里很明显。if语句里的-n判断其字符串参数是否为空,不为空的话则返回真(true)。下面是这个脚本运行后的结果:

$ sh readexample Enter your name: Ron Hello Ron!
2.2.3 命令行参数和函数
给一个脚本的命令行参数可以成为变量,这些变量的名字是数字。$1是第一个命令行参数,$2是第二个,以此类推。$0是调用该脚本所采用的名字。这个名字可以是像../bin/example.sh这样的奇怪名字,所以它的取值并不固定。

变量$#是提供给脚本的命令行参数的个数,变量$*里保存有全部参数。这两个变量都不包括或者算上$0。

如果调用的脚本不带参数,或者参数不正确,那么该脚本应该打印一段用法说明,提醒用户怎样使用它。下面这个脚本的例子接受两个参数,验证这两个参数都是目录,然后显示它们。如果参数无效,那么这个脚本会打印一则用法说明,并且用一个非零的返回码退出。如果调用这个脚本的程序检查该返回码,那么它就会知道这个脚本没有正确执行。

!/bin/bash

function show_usage {

echo "Usage:  $0  source_dir dest_dir" exit  1

}

 Main  program starts here if [ $#  -ne  2 ]; then

show_usage

else  # There are two arguments if [ -d  $1  ]; then

source_dir=$1

 else

fi

 echo 'Invalid source directory' show_usage

 if [ -d  $2  ]; then dest_dir=$2

 else

fi fi

 echo 'Invalid destination  directory' show_usage

 printf "Source  directory is  ${source_dir}\n" printf "Destination directory is  ${dest_dir}\n"

我们创建了一个单独的show_usage函数,用它打印用法说明。如果这个脚本以后又做了更新,能够接受更多的参数,那么只要在一个地方修改用法说明就行了3。

$ mkdir  aaa  bbb

$ sh  showusage aaa  bbb Source directory is  aaa Destination directory is  bbb

$ sh  showusage foo  bar

Invalid source directory

Usage: showusage  source_dir dest_dir

bash函数的参数就按命令行参数那样处理。第一个参数变成$1,以此类推。正如上面的例子所示,$0是这个脚本的名字。

要让上面的例子更健壮一点儿,我们可以编写show_usage函数,让它接受一个出错码作为参数。对于执行不成功的每一种不同类型,返回一个定义好的出错码。下面的代码片段给出了该函数的样子。

function show_usage {

echo "Usage:  $0  source_dir dest_dir" if [ $#  -eq  0 ]; then

exit  99  # Exit  with arbitrary nonzero return code

 else

fi

}

 exit  $1

下面这个版本的函数,其参数可有可无。在一个函数内部,$#表明传入了多少个参数。如果没有提供更确定的出错码,那么这个脚本就返回代码99。但是如果给这个函数一个确定的出错码值,就会让脚本在打印用法说明之后以那个出错码退出,例如:

show_usage  5

(shell变量$?是上次执行的命令退出的状态,而且无论该命令是在一个脚本内部使用,还是在命令行上使用。)

在bash里,函数和命令之间很类似。用户可以在自己的~/.bash_profile文件里定义自己的函数,然后在命令行上使用它们,就好像它们是命令一样。例如,如果站点里统一将网络端口7988用于SSH协议(“不公开,即安全”的一种形式),就可以在~/.bash_profile文件里定义

function ssh {

/usr/bin/ssh -p  7988  $*

}

以保证ssh总是带选项-p7988来运行。和许多shell一样,bash也有一种别名机制,能更加简洁地再现上面这个限制端口的例子,不过采用函数的方法更通用,功能也更强。忘掉别名,采用函数吧。

2.2.4 变量的作用域
在脚本里的变量是全局变量,但是函数可以用local声明语句,创建自己的局部变量。考虑下面的代码:

#!/bin/bash

function localizer {

echo "==> In  function localizer, a starts as ' $a' " local a

echo "==> After local declaration, a is  ' $a' " a="localizer version"

echo "==> Leaving localizer, a is  ' $a' "

}

a="test"

echo "Before  calling localizer, a is  ' $a' " localizer

echo "After  calling localizer, a is  ' $a' "

下面的日志显示在localizer函数内,局部变量$a屏蔽了全局变量$a。在localizer内,在碰到local声明了局部变量$a之前,全局变量$a都可见;local实际上是一条命令,它从执行的那个地方开始,创建局部变量。

$ sh  scopetest.sh

Before calling localizer, a is  'test'

==>  In  function localizer, a starts as 'test'

==>  After local declaration, a is  ' '

==>  Leaving localizer, a is  'localizer version' After calling localizer, a is  'test'

2.2.5 控制流程
我们在本章里已经见过几种if-then和if-then-else语句的形式;它们的功能在其名字中得以体现。一条if语句的结束标识是fi。要把几条if语句串起来,可以用elif这个关键字,它的意思是“else if”。例如:

if [ $base -eq  1 ] && [ $dm -eq  1 ]; then installDMBase

elif  [ $base -ne  1 ] && [ $dm -eq  1 ]; then installBase

elif  [ $base -eq  1 ] && [ $dm -ne  1 ]; then installDM

 else

fi

 echo '==>  Installing nothing'

用[]做比较的奇特语法,以及整数比较运算符的名字(例如,-eq),都看上去像是命令行选项,它们二者都是从原来Bourne shell的/bin/test命令延续下来的。方括号实际上是调用test的一种快捷方式,而不是if语句的语法要求4。

表2.2给出了bash的数值和字符串比较运算符。bash比较数值采用文字运算符,而比较字符串采用符号运算符,这正好和Perl相反。

bash对文件属性取值的那些选项是其出彩之处(还是其/bin/test遗留下来的特性)。bash有大量的测试文件和比较文件的运算符,表2.3列出了其中几个。

虽然elif的形式能用,但是为了清楚起见,用case语句做选择是更好的方法。case的语法如下面的这个函数例程所示,该函数集中给一个脚本写日志。特别值得注意的是,每一选择条件之后有个右括号,而在条件符合时每个要执行的语句块之后有两个分号。case语句以esac结尾。

The log  level is  set in  the global variable LOG_LEVEL. The  choices

 are, from most to  least severe, Error, Warning, Info,  and Debug.

function logMsg  { message_level=$1 message_itself=$2
if [ $message_level -le  $LOG_LEVEL ]; then case $message_level in

0) message_level_text="Error" ;;

1) message_level_text="Warning"  ;;

2) message_level_text="Info" ;;

3) message_level_text="Debug" ;;

*)  message_level_text="Other"

esac

echo "${message_level_text}:  $message_itself" fi

}

这个函数演示了许多系统管理应用经常采取的“日志级别”方案。脚本的代码产生详尽程度不同的日志消息,但是只有那些在全局设定的阈值$LOG_LEVEL之内的消息才被真正记录到日志里,或者采取相应的行动。为了阐明每则消息的重要性,在消息文字之前用一个标签说明其关联的日志级别。

2.2.6 循环
bash的for…in结构可以让它很容易对一组值或者文件执行若干操作,尤其是和文件名通配功能(对诸如和?这样的模式匹配字符进行扩展,形成文件名或者文件名的列表)联合起来使用的时候。在下面这个for循环里,其中的.sh模式会返回当前目录下能够匹配的文件名列表。for语句则逐一遍历这个列表,接着把每个文件名赋值给变量$script。

#!/bin/bash

suffix=BACKUP--`date +%Y%m%d-%H%M`

for  script in  *.sh; do newname=”$script.$suffix”

echo  "Copying $script  to  $newname..." cp  $script $newname

done

输出结果如下:

$ sh  forexample

Copying rhel.sh to  rhel.sh.BACKUP--20091210-1708... Copying sles.sh to  sles.sh.BACKUP--20091210-1708...

…

在这里的上下文关系中,对文件名做扩展并没有什么玄妙之处;它的做法就和在命令行上一模一样。也就是说,先扩展,然后再由解释器处理已经扩展过的这一行5。也可以静态地输入文件名,就像下面这行一样。

for  script in  rhel.sh sles.sh; do

实际上,任何以空白分隔的对象列表,包括一个变量的内容,都可以充当for …in语句的目标体。

bash也有从传统编程语言看来更为熟悉的for循环,在这种for循环里,可以指定起始、增量和终止子句。例如:

for  (( i=0  ; i < $CPU_COUNT  ; i++  )); do

CPU_LIST="$CPU_LIST  $i" done

接下来的例子演示了bash的while循环,这种循环也能用于处理命令行参数,以及读取一个文件里的各行。

#!/bin/bash exec 0<$1

counter=1

while read line;  do

echo "$counter: $line"

$((counter++))

done

下面是输出结果:

ubuntu$ sh  whileexample /etc/passwd

1: root:x:0:0:Superuser:/root:/bin/bash

2: bin:x:1:1:bin:/bin:/bin/bash

3: daemon:x:2:2:Daemon:/sbin:/bin/bash

…

这个脚本片段有两个有趣的功能。exec语句重新定义了该脚本的标准输入,变成由第一个命令行参数指定的任何文件6。这个文件必须要有,否则脚本就会出错。

在while子句里的read语句实际上是shell的内置命令,但它的作用就和一条外部命令一样。外部命令也可以放在while子句里;在这种形式下,当外部命令返回一个非零的退出状态时,就会结束while循环。

表达式$((counter++))实际上是个丑小鸭。$((…))这样的写法要求强制进行数值计算。它还可以利用$来标记变量名。++是人们在C和其他语言中熟悉的后置递增运算符。它返回它前面的那个变量的值,但返回之后还要把这个变量的值再加1。

$((…))的技巧在双引号里也起作用,所以可以把整个循环体紧凑地写到一行里。

while read line;  do

echo "$((counter++)):  $line" done

2.2.7 数组和算术运算
复杂的数据结构和计算不是bash的特长。但它的确至少提供了数组和算术运算。

所有bash变量的值都是字符串,所以bash在赋值的时候并不区分数字1和字符串“1”。不同之处在于如何使用变量。下面几行代码展示出了其中的差异:

#!/bin/bash

a=1 b=$((2))

c=$a+$b d=$(($a+$b))

echo "$a  + $b  = $c  \t(plus sign as string literal)"

echo "$a  + $b  = $d  \t(plus sign as arithmetic addition)"

This script produces the output

这个脚本产生的输出如下:

1 + 2 = 1+2   (plus  sign  as string literal)

1 + 2 = 3  (plus  sign  as arithmetic addition)

注意给$c赋值的语句,其中的加号(+)连字符串的连接运行符都不是。它仅仅就是一个字符而已。那行代码等价于

c="$a+$b"

为了强制进行数值计算,要把这个表达式放在$((…))里面,就像上面给$d赋值那样。但即便如此,也不会让$d获得一个数值;它的值仍然保存为字符串“3”。

bash通常能够混合使用算术、逻辑和关系运算符;参考手册页了解详情。

bash中的数组有点儿怪,所以不常用到它们。然而,如果需要它们也依然可以用。数组用括号括起来,数组元素之间用空白隔开。数组元素中的空白要用引号引起来。

ample=(aa 'bb  cc'  dd)

单个数组元素用${array name [subscript]}来访问。下标从0开始。下标和@指整个数组,${#array name[]}和${#array name[@]}这两种特殊形式表示数组里元素的个数。不要把它们和似乎更合乎逻辑的${#array name}搞混了;后者实际上是数组第一个元素的长度(等价于${#array name[0]})。

读者可能会以为$example[1]是指数组的第二个元素,这一点无可争议,但bash对这个字符串的分析结果却是:$example(即$example[0]的简洁引用形式)加上一个字符串[1]。在访问数组变量的时候,一定要带花括号——这一点无一例外。

下面是一个快速脚本,它演示了bash中数组管理的一些功能和缺陷:

#!/bin/bash

example=(aa 'bb  cc'  dd)

example[3]=ee

echo "example[@] = $\{example[@]}"

echo "example array contains $\{#example[@]} elements"

for  elt  in  "$\{example[@]}";  do echo "    Element  = $elt"

done

这个脚本的输出如下:

$ sh arrays

example[@] = aa bb  cc  dd  ee example array contains 4 elements

Element = aa Element = bb  cc Element = dd Element  = ee

这个例子似乎很直观易懂,但只是因为我们已经把这个脚本构造得循规蹈矩了。人们一不小心就会犯错误。例如,用下面这一句替换for语句那行代码:

for elt  in  $\{example[@]};  do

(在数组表达式外面没有用引号引起来)也能行,但它却不是输出4个数组元素,而是5个:aa、bb、cc、dd和ee。

这背后的问题是,因为所有bash变量实质上仍然是字符串,所以数组的表象充其量还是不确定的。字符串什么时候分割成数字元素,怎样分割成数组元素,都有很多细微变化。读者可以使用Perl或者Python,或是用谷歌搜索Mendel Cooper的Advanced Bash-Scripting Guide来研究这些细微差别。

时间: 2024-11-02 23:26:33

《UNIX/Linux 系统管理技术手册(第四版)》——2.2 bash脚本编程的相关文章

《UNIX/Linux 系统管理技术手册(第四版)》——第2章 脚本和shell 2.1 shell的基础知识

第2章 脚本和shell 好的系统管理员都要写脚本.脚本以标准和自动的方式履行系统管理员的繁杂事务,藉此把管理员的时间节省出来,以花在更重要和更有意思的任务上.从某种意义上讲,脚本也是一种低质量的文档,因为它们充当了一种权威提纲,提纲里列出完成特殊任务所需的步骤. 从复杂性来看,系统管理脚本的范围很广,小到一个脚本,简单得只封装几条静态命令,大到一个重要的软件项目,为整个站点管理主机配置和管理性数据.在本书里,我们所感兴趣的主要是系统管理员通常会碰到的较小的日常脚本项目.因此,对于较大项目才需要

《UNIX/Linux 系统管理技术手册(第四版)》——2.7 推荐读物

2.7 推荐读物 UNIX/Linux 系统管理技术手册(第四版) BROOKS, FREDERICK P., JR. The Mythical Man-Month: Essays on Software Engineer- ing. Reading, MA: Addison-Wesley, 1995. Shell基础知识和bash脚本编程 ALBING, CARL, JP VOSSEN, AND CAMERON NEWHAM.Bash Cookbook. Sebastopol, CA: O'R

《UNIX/Linux 系统管理技术手册(第四版)》——1.2 读者的知识背景

1.2 读者的知识背景 UNIX/Linux 系统管理技术手册(第四版) 本书假定读者已经具备一定程度的Linux或者UNIX经验.尤其要从用户的角度对系统的风格有个一般性的概念,因为我们不再复述这方面的材料.有一些优秀的书籍可以帮助读者迅速掌握这些概念,参见本章1.14节. 即便在现今有三维图形显示功能的桌面计算机上,用于UNIX和Linux系统管理的GUI工具与其下层丰富的软件相比,依然显得相当简单.在现实世界里,我们仍然要通过编辑配置文件和编写脚本来完成管理工作,因此读者需要习惯于使用某种

《UNIX/Linux 系统管理技术手册(第四版)》——2.6 脚本编程的最佳实践

2.6 脚本编程的最佳实践 UNIX/Linux 系统管理技术手册(第四版) 虽然本章里的代码片段几乎不带注释,而且很少打印用法说明,只是因为我们已经列出了每个例子的大纲,从而体现出若干关键点.实际的脚本应该有更好的表现.有几本书通篇就讲编码的最佳实践,不过其中的基本指导原则如下. 如果运行脚本时带了不合适的参数,脚本应该打印一则用法说明,然后再退出.更好的做法是,也以这样的方式实现--help选项. 验证输入的有效性,并检查获得的输入值.例如,在对算出来的一个路径执行rm -fr操作之前,可能

《UNIX/Linux 系统管理技术手册(第四版)》——2.4 Perl编程

2.4 Perl编程 UNIX/Linux 系统管理技术手册(第四版) Larry Wall发明了Perl语言,它第一种真正伟大的脚本编程语言.它的能耐要比bash大得多,而且编写良好的Perl代码也相当容易阅读.另一方面,Perl没有给开发人员强加太多的风格规范,所以不考虑可读性的Perl代码显得很神秘.Perl也被诟病为只适合写(不适合读)的语言. 我们在这里介绍Perl 5,这个版本成为标准已经有10年了.Perl 6是一个仍处在开发之中的主要版本.参考perl6.org了解详情. 对于系

《UNIX/Linux 系统管理技术手册(第四版)》——1.14 推荐读物

1.14 推荐读物 UNIX/Linux 系统管理技术手册(第四版) ROBBINS, ARNOLD. UNIX in a Nutshell (4th Edition). Sebastopol, CA: O'Reilly Me- dia, 2008. SIEVER, ELLEN, AARON WEBER, AND STEPHEN FIGGINS.Linux in a Nutshell (5thEdition). Sebastopol, CA: O'Reilly Media, 2006. GANC

《UNIX/Linux 系统管理技术手册(第四版)》——1.6 特定于系统的管理工具

1.6 特定于系统的管理工具 UNIX/Linux 系统管理技术手册(第四版) 在现代的操作系统内,包含有各种各样可视化的工具和控制面板(比如SUSE的YaST2和IBM的SMIT),帮助用户配置或者管理选定的系统功能.这些工具非常有用,特别是对于管理员新手来说更是如此,但是它们也倾向于不能完整地体现下层软件的实际操作.它们让管理工作变得容易,但大多数又变得不那么权威. 在本书中,出于下面几个原因,我们要介绍可视化工具调用的底层机制,而不是工具本身.第一,可视化工具趋于专有化(或者至少是趋于为系

《UNIX/Linux 系统管理技术手册(第四版)》——第1章 从何处入手 1.1 系统管理员的基本任务

第1章 从何处入手 UNIX/Linux 系统管理技术手册(第四版) 现如今,有关UNIX和Linux的知识太多太多,由手册.博客.期刊.图书以及其他参考资料构成了浩如烟海的知识库,因此,我们在策划本书内容的时候,就从上述的知识库中遴选出沧海一粟,力求专门满足系统管理员的需求. 首先,本书是一本入门培训指南.书中探讨了主要的管理性系统,先区分它们彼此间的不同之处,然后又阐述它们如何协同工作.在很多情况下,同一概念有若干种不同的实现,系统管理员必须要在其中做出自己的选择,我们则介绍每种实现的优缺点

《UNIX/Linux 系统管理技术手册(第四版)》——1.10 其他的权威文档

1.10 其他的权威文档 UNIX/Linux 系统管理技术手册(第四版) 手册页仅仅是官方文档中的很小一部分.遗憾的是,其余更大一部分的文档都散布在Web上. 1.10.1 针对系统的专门指南 大多数发行商都有自己专门的文档项目,许多发行商还出整本书那样的手册.现在,一般都能找到联机形式的手册,而不是纸质的书.文档的规模和质量则大有不同,但是大多数发行商都至少提供一份系统管理指南和一份安装指南.表1.4给出了在哪儿可以找到我们示例系统的文档. 在这其间最出众的是IBM,IBM针对系统管理的各方