命令chmod用法

chmod用法  时间:2021-04-05  阅读:()

我们首先以一个具有针对性的最新课程作为开始,随后对本书中所用到的技术进行简要说明.
这些说明涉及的内容非常广泛,从逐行处理文件的最快方式到UNIX和shell脚本的大小写区分.
虽然没有包括脚本编程的所有方面,但却是一个很好的开端,而且给出了本书中一个主题的例子.
对于在本章中列出的每个主题,在本书后面的章节中都会有一个详细的说明.

我们鼓励读者完整地学习完本书.
书中的每一章都使用不同方式对不同的主题进行了讨论.
这样做的目的是为了强调绝不是只有一种技术可以解决UNIX中的问题.
本书中的所有shell脚本都是解决实际应用中某个问题的例子.
读者在阅读本书时,会发现书中讨论了UNIX中的大多数常见任务(也有一些不太常见的任务).
所有的shell脚本都对思考问题的过程进行了良好的解释,而且我们总是以正确的命令语法作为开始,来编写针对某个特定目的的shell脚本.
希望读者在阅读本书时能够和我编写本书时一样感到快乐.
下面让我们开始吧!

1.
1区分大小写UNIX是区分大小写的.
因此,我们编写的shell脚本也区分大小写.
1.
2UNIX特殊字符下面所有的字符都具有特殊的意义或功能.
如果它们以一种不需要其特殊意义的方式来使用,则必须进行转义(escaped).
为进行转义或去除它的特殊功能,这些字符必须在前面带有一个反斜杠\,或者使用单引号括起来.

1.
3shellshell(命令行解释器)是一个能够用来运行命令、程序和shell脚本的环境.
shell具有不同的版本,就像操作系统具有不同的版本一样.
每种版本的shell都具有各自可以识别的命令集和函数集,本书使用的是Bourne、Bash和Kornshell.
根据UNIX版本和版本号的不同,shell位于/usr/bin目录或/bin/目录下,如表1-1所示.
表1-1不同UNIX中shell所在目录shell目录Bourne/bin/sh或/usr/bin/shBash/bin/Bash或/usr/bin/BashKorn/bin/ksh或/usr/bin/ksh1.
4shell脚本shell脚本的基本概念是一组命令,这些命令按照执行的顺序被列出.
一个良好的shell脚本应该具有注释,注释以一个英镑符号或散列符号#作为开始,用来描述执行步骤.
在shell脚本中可能具有条件测试(例如值A大于值B),允许我们处理大量数据的循环、用文件和变量读取和存储数据,当然还可能包括函数(function).

在接下来的讨论中将编写许多脚本,因此读者从一开始就应该在脑子里有个明确的目标,一旦有了确定的目标,就可以为脚本明确具体的用途,确定一系列预期的结果.
在解决问题的过程中,还会发现一些技巧和窍门,当然还有思路的转变,即采用一种可能相反的方法来获得相同的结果.
各种技术的选择都是灵活多变的.

shell脚本和函数都是解释型的(interpreted),这意味着它们不能被编译.
shell脚本和函数都是ASCII文本,可以由shell命令解释器读取.
当执行一个shell脚本或函数时,命令解释器将逐行、逐个循环或逐个测试地读取所有的ASCII文本,并从头到尾逐行执行每条语句.

1.
5函数函数的编写和shell脚本的编写方法非常类似,不同之处在于,在大多数情况下,函数是在shell脚本中定义或编写的,而且在脚本中被调用.
通过这种方式,可以编写一个需要多次使用的代码段,这样就可以在编写过一次后重复使用它们,不必每次都重新编写该代码,而只需调用函数即可.

还可以在系统一级定义函数,这样就可以在系统环境下一直使用这个函数,但这是以后要讨论的主题.
函数具有如下形式.
functionfunction_name{commandstoexecute}或function_name(){commandstoexecute}当在脚本中编写函数时,必须记住要在使用之前对其进行声明或定义.
函数部分必须出现在调用函数的命令语句之前.
不能调用尚不存在的函数.
1.
6运行shell脚本shell脚本可以按照以下方式来执行.
kshshell_script_name以上命令将创建一个Kornshell,并在新创建的Kornshell环境中执行shell_script_name.
这对于sh和Bashshell脚本也适用.
shell_script_name如果在文件中设置执行位为1(参见chmod命令的参考页面,manchmod),那么以上命令将执行shell_script_name.
脚本将在当前shell脚本的第一行所声明的shell中执行.
如果在shell脚本的第一行没有声明shell,则脚本将在默认的shell中执行,即用户所在系统定义的shell.
在一个非预期的shell中执行脚本可能会导致失败或无法预料的结果.

注意,一定要声明shell.
如果想对脚本的运行方式以及在哪个shell下运行进行完全的控制,则必须在shell脚本的第一行对shell进行声明.
如果没有声明,则脚本将在默认shell中执行,即用户所在的系统为执行shell脚本定义的shell.
例如,如果脚本被编写为在Bashshell,Bash中运行,而默认运行shell脚本的shell为Cshell,csh,那么脚本在执行过程中很可能会出错.
要声明一个shell,必须在shell脚本的第一行出现如表1-2所示的声明语句.
表1-2声明不同类型的shell命令描述#!
/bin/sh或#!
/usr/bin/sh声明一个Bourneshell#!
/bin/ksh或#!
/usr/bin/ksh声明一个Kornshell#!
/bin/csh或#!
/usr/bin/csh声明一个Cshell#!
/bin/Bash或#!
/usr/bin/Bash声明一个Bourne-Again(Bash)shell1.
7shell脚本中的注释和风格本书始终强调脚本中要编写好的注释.
在脚本的作者看来直观明了的内容对于其他用户可能是晦涩难懂的.
我们编写出的代码,必须便于阅读而且逻辑清晰.
这涉及到编写一个易于阅读和维护的脚本,意味着必须加入大量的描述步骤的注释.
大部分情况下,编写shell脚本的人并不负责维护脚本.
而对于维护脚本的人来说,最坏的事情莫过于去揣摩其他人编写的没有注释的、根本不清楚每一步想要做什么的代码.
修改他人的代码是一件非常困难的事情,因为不得不费劲地揣摩脚本作者的思路,所以有时我们会考虑干脆重新编写整个shell脚本.
通过编写一个清晰可读的脚本,插入大量的描述作者思路以及如何使用输入、输出、变量和文件的注释,就可以避免以上的困境.

命令语句的一个良好的风格是可读性强,因此,有时最好将一个命令分成若干行,而不是将所有的命令或管道放在同一行.
有时候,最好是创建一个长的管道,而有时使用管道及确定预期的结果对一个脚本编写新手而言可能会有困难.
不管怎样,脚本中应当具有逐步描述编写者思路的注释.
这样,以后需要读取这些代码的人在看到注释而理解了作者思路时将能够高兴地说"嘿,这样做真是不错!
"命令的可读性和逐步骤的注释是编写一个好脚本的基础.
如果在代码中使用了大量的注释,即使6个月之后重新阅读编写的代码,也会感觉轻松许多.
相信我,一定需要重新阅读这些代码的.
别忘了在编写代码时注释所有的内容,包括但不限于描述变量和文件的用途,以及循环的作用.
描述每个测试,还可能包括预期的结果,以及操作数据和许多数据字段的方式.
当然在每行的注释之前要有一个散列符号#.

下面给出的脚本stub位于本书网站www.
wiley.
com/go/michael2e上,其名称为script.
stub.
它具有开始编写一个shell脚本时所需要的所有注释.
script.
stub文件可以被复制到一个新的文件中.
编辑新的文件名,然后在其中编写代码.
下面的程序列表1-1列出了script.
stub文件.

程序列表1-1script.
stubshell脚本开始部分#!
/bin/Bash##SCRIPT:NAME_of_SCRIPT#AUTHOR:AUTHORS_NAME#DATE:DATE_of_CREATION#REV:1.
1.
A(ValidareA,B,D,TandP)ForAlpha,Beta,Dev,TestandProduction)##PLATFORM:(SPECIFY:AIX,HP-UX,Linux,OpenBSD,SolarisorNotplatformdependent)##PURPOSE:Giveaclear,andifnecessary,long,descriptionofthepurposeoftheshellscript.
Thiswillalsohelpyoustayfocusedonthetaskathand.
##REVLIST:DATE:DATE_of_REVISIONBY:AUTHOR_of_MODIFICATIONMODIFICATION:Describewhatwasmodified,newfeatures,etc--###set-n#Uncommenttocheckscriptsyntax,withoutexecution.
NOTE:Donotforgettoputthecommentbackinortheshellscriptwillnotexecute!
#set-x#Uncommenttodebugthisshellscript#DEFINEFILESANDVARIABLESHEREDEFINEFUNCTIONSHEREBEGINNINGOFMAIN#Endofscript程序列表1-1中显示的shell脚本提供了一个开始编写shell脚本的框架,其中包含了声明变量和文件、创建函数的部分,之后编写shell脚本主体的最后一个部分,即BEGINNINGOFMAIN.
1.
8控制结构下面是广泛使用的控制结构.
if.
.
.
then语句if[test_command]thencommandsfiif.
.
.
then.
.
.
else语句if[test_command]thencommandselsecommandsfiif…then…elif…(else)语句if[test_command]thencommandselif[test_command]thencommandselif[test_command]thencommands.
.
.
else(Optional)commandsfifor…in语句forloop_variableinargument_listdocommandsdonewhile语句whiletest_condition_is_truedocommandsdoneuntil语句untiltest_condition_is_truedocommandsdonecase语句case$variableinmatch_1)commands_to_execute_for_1;;match_2)commands_to_execute_for_2;;match_3)commands_to_execute_for_3;;.
.
.
*)(Optional-anyothervalue)commands_to_execute_for_no_match;;esac注意:case语句的最后一部分:*)commands_to_execute_for_no_match;;是可选的.
1.
9使用break、continue、exit和return语句有时需要从某个for或while循环中中断(break),或者继续(continue)执行下一个代码块,或者退出(exit)整个脚本,或者将函数执行结果返回(return)给调用函数的脚本.

break语句用于在结束执行该语句之前的所有代码行之后中断整个循环的执行.
程序将继续执行位于循环体结尾处之后的代码.
continue语句用于将控制转移到下一组代码,但是会继续执行循环.
exit语句将完成我们所预料的结果:退出整个脚本.
可能会在exit命令之后加入一个整数(如exit0),该整数将作为返回代码发送给系统.
return语句用于在函数中将数据返回,或返回一个结果或代码给调用脚本.
1.
10here文档here文档(heredocument)用于将输入重定向到某个交互式shell脚本或程序.
通过为交互式脚本或程序提供所需要的输入,可以在shell脚本中运行某个交互式程序或脚本,而不需要用户介入.
这就是为什么将其称为here文档的原因:所需的输入位于此处(here),而不是在其他地方.

以下是here文档的语法结构.
program_name>追加到文件末尾>重定向、创建或覆盖文件|管道符,连接不同命令(续表)命令描述||逻辑或——command1||command2——如果command1失败,则执行command2&后台执行&&逻辑与——command1&&command2——如果command1成功,则执行command2date显示系统日期和时间echo将字符串写入标准输出sleep将执行过程中断指定的秒数wc计算文件里的单词、行和字符数head查看文件开头tail查看文件结尾diff比较两个文件sdiff并行比较两个文件(需要132个字符显示)spell拼写检查lp、lpr、enq、qprt打印文件lpstat系统打印队列的状态enable启用或启动打印队列disable禁用或停止打印队列cal显示日历who显示系统用户信息w扩展的who命令whoami显示$LOGNAME或$USER环境变量whoamI显示登录名称、终端、登录日期/时间和登录位置f、finger显示用户登录信息,包括user.
plan和.
projecttalk使两个用户能够进行分屏对话write在屏幕上显示用户信息wall在屏幕上显示所有用户登录信息rwall向远程机所有用户显示信息rsh或remsh在远程机上执行一个命令或登录df显示文件系统统计数字ps显示当前运行的进程信息netstat显示网络状态vmstat显示虚拟内存状态iostat显示输入/输出状态uname显示当前操作系统名称和机器信息(续表)命令描述sar报告系统活动情况basename显示字符串参数的基本文件名man显示联机参考手册su转切换到另一个用户,也称为超级用户cut写入所选字符awk解析字符的编程语言sed用于字符置换的编程语言vi启动vi编辑器emacs启动emacs编辑器1.
12符号命令表1-4中显示的符号实际上是命令,本书广泛使用了该表中的所有符号命令.
表1-4符号命令命令描述()在一个子shell脚本里运行括号内的命令(())在某个shell中对一个变量求值或赋值,并进行数学运算$(())求括号内表达式的值[]同test命令用于字符串比较$()命令替换'command'命令替换1.
13变量变量是可以进行赋值的字符串.
赋给变量的值可以是数字、文本、文件名、设备或任何其他类型的数据.
变量其实是一个指向实际数据的指针.
本书的脚本中将大量使用变量,而只有极少数的脚本不使用变量.
本书中以大写形式来指定变量——例如UPPERCASE.
但是,在实际的shell编程中并不建议使用大写的变量名,因为这些大写的变量名可能会与系统中的大写环境变量名相冲突.
在本书中使用大写的变量名主要是为了突出它们,让它们在代码中更醒目而已.
当读者编写自己的脚本或修改书中的脚本时,注意将变量名改为小写.
将数据赋给变量,使用UPPERCASE="value_to_assign"作为赋值语法.
为访问变量,例如UPPERCASE所指代的数据,必须加上一个美元符号$来作为前缀——例如$UPPERCASE,为查看赋值给变量的数据,使用echo$UPPERCASE、print$UPPERCASE作为命令结构,如果变量指向一个文件,则使用cat$UPPERCASE作为命令结构.
1.
14命令行参数命令行参数$1,$2,$3,…,$9是位置参数,$0指向实际的命令、程序、shell脚本或函数,$1,$2,$3,…,$9作为命令的参数.
在一个函数中出现的位置参数$0,$2等由函数本身使用,可能不会在调用函数的shell脚本环境中出现.
变量在函数或shell脚本中的可知范围称为变量的作用域(scope).
1.
15shift命令shift命令用于将位置参数向左移动,例如,shift命令使得$2成为$1.
也可以为shift命令加入一个数字来移动多个位置,例如,shift3使得$4移动到$1的位置.
有时,我们会遇到传递给某个shell脚本或函数的参数$1,$2,$3,…(也称为位置参数)个数未知的情况.
使用shift命令就是一种按照参数列出的顺序来处理每个位置参数的好的方式.
为更进一步解释shift命令,我们将说明如何处理传递给shell脚本的参数个数未知这种情况,如程序列表1-2所示.
读者应认真研究本示例中的shell脚本结构.
在该脚本中使用shift命令来处理未知数目的命令行参数或位置参数,在该脚本中将它们称为token(标志).

程序列表1-2使用shift命令的例子#!
/usr/bin/sh##SCRIPT:shifting.
sh##AUTHOR:RandyMichael##DATE:12/30/2007##REV:1.
1.
A##PLATFORM:Notplatformdependent##PURPOSE:Thisscriptisusedtoprocessallofthetokenswhich#arepointedtobythecommand-linearguments,$1,$2,$3,etc.
.
.
##REV.
LIST:##InitializeallvariablesTOTAL=0#InitializetheTOTALcountertozero#StartawhileloopwhiletruedoTOTAL=`expr$TOTAL+1`#Alittlemathinthe#shellscript,arunning#totaloftokensprocessed.
TOKEN=$1#Wealwayspointtothe$1argumentwithashiftprocesseach$TOKENshift#Grabthenexttoken,i.
e.
$2becomes$1doneecho"Totalnumberoftokensprocessed:$TOTAL"本书后面将会更详细地讨论一个类似的shift命令示例.
1.
16特殊参数$*和$@特殊参数$*和$@允许一次访问所有的命令行参数.
除非使用双引号""将它们括起来,否则$*和$@具有相同的功能.
特殊参数定义如下.
特殊参数$*指定了所有的命令行参数.
特殊参数$@也指定了所有的命令行参数.
特殊参数"$*"将整个参数列表作为一个参数来获取.
特殊参数"$@"获取整个参数列表,并将其分隔成不同的参数.
重新编写程序列表1-2中所显示的shell脚本,使用特殊参数$*和$@来处理未知数目的命令行参数,如程序列表1-3所示.
程序列表1-3使用特殊参数$*的例子#!
/usr/bin/sh##SCRIPT:shifting.
sh#AUTHOR:RandyMichael#DATE:12-31-2007#REV:1.
1.
A#PLATFORM:Notplatformdependent##PURPOSE:Thisscriptisusedtoprocessallofthetokenswhich#Arepointedtobythecommand-linearguments,$1,$2,$3,etc.
.
.
-##REVLIST:###StartaforloopforTOKENin$*doprocesseach$TOKENdone在以上代码中使用特殊参数$@也很容易,正如在以上代码段中所看到的,使用$*和$@是针对前面同一问题的不同解决方案,而且要编写的代码更少.
使用$*和$@的任何一种方式都可以完成相同的任务.

1.
17双引号"、符号'和`如何在脚本、函数和命令语句中使用这些符号这些符号在编写脚本时很容易使人困惑.
现在来说明这些符号的使用方法.
根据任务和所期望结果的不同,使用正确的包含符号是非常重要的.
如果使用不正确,可能会出现意想不到的结果.
在允许字符替换或命令替换的语句中,可以使用双引号",当定义一个包含空格数据的变量时,需要使用双引号,如下所示.
NAME="RandalK.
Michael"如果漏掉了双引号,会得到如下错误.
NAME=RandalK.
Michael-Bash:K.
:commandnotfound在不允许字符替换或命令替换的语句中,可以使用',使用'括起来的目的是希望在变量或命令语句中使用文字正文(literaltext),而不进行任何替换.
所有特殊的含义或功能都被去除了.
此外,若希望每次使用某个变量时都重新读取,也可以使用',例如,'$PWD'在处理命令行提示符PS1时多次被用到.
此外,在某个字符串之前使用一个反斜杠\,也可以去除某个字符或字符串的特殊含义.

在希望执行某个命令或脚本并替换其输出结果(这是一种命令替换)的语句中,可以使用符号`.
`键在大多数键盘上位于1键的左侧,Esc键的下面.
命令替换也可以使用$(command)命令语法来实现.
在本书中将看到许多这样的例子.

1.
18在Solaris中使用awk本书中经常使用awk解析整个文本行.
而在Solaris系统中有些特殊,必须使用nawk.
如果需要指定一个不同于默认字段分隔符(空白)的分隔符,就要使用awk-F:.
例如,awk语句在Solaris机制下是无效的,如果发现UNIX版本是Solaris,为了能够解决这个问题,就必须使用nawk.
把下面的代码段添加到你所编写的shell脚本的变量声明部分就可以解决这个问题.

#Setupthecorrectawkusage.
Solarisneedsto#usenawkinsteadofawk.
case$(uname)inSunOS)aliasawk=nawk;;esac1.
19正确使用echo命令使用echo命令显示文本.
echo命令使用反斜杠操作符进行光标操作:\n控制光标到新的一行,\c控制光标继续在本行,\b控制光标后退,\t控制光标到下一个制表位,\r控制光标返回,\v控制光标在各行间垂直移动.
在Kornshell里,echo命令默认识别这些命令选项.
在Bashshell里,必须给echo命令后面添加-e,例如echo-e"\n"表示一个新行.
可以在脚本里通过输入$SHELL变量要求系统执行shell脚本.
即使限定Kornshell在脚本的第一行,许多Linux版本也在Bashshell中执行.
因为Bashshell需要使用echo-e启用反斜杠操作符,如果正执行的shell脚本是*/bin/Bash,可以使用一个case语句将echo语句替换成echo-e语句.
这样当需要使用echo命令时,就确保能够正确显示文本.
将下面的代码段添加到所有Kornshell脚本的变量声明部分,问题就会得以解决.
#Setupthecorrectechocommandusage.
ManyLinux#distributionswillexecuteinBashevenifthe#scriptspecifiesKornshell.
Bashshellrequires#weuseecho-ewhenweuse\n,\c,etc.
case$SHELLin*/bin/Bash)aliasecho="echo-e";;esac1.
20shell脚本中的数学可以在shell脚本中容易地进行数学运算.
shell的let命令和((expr))命令表达式是对某个整数表达式进行求值的最常用方法,后面还将介绍用bc函数来进行浮点数学运算.
shell使用了C编程语言中的算术运算符(如表1-5所示),其中的优先级以降序排列.
表1-5数学运算符运算符描述++--自增和自减,可作为前缀或后缀+一元加法-一元减法!
~逻辑非;二进制取反(某个数的补码)*/%乘法、除法、取模(求余数)+-加法、减法(续表)运算符描述>左移位、右移位=小于或等于、大于或等于小于、大于==!
=相等、不相等(都是从左到右求值)&按位与运算^按位异或运算|按位或运算&&逻辑与运算||逻辑或运算本书使用了许多这样的数学运算符,但没有全部使用.
在本书中,我们希望使内容简单直接,不希望使用晦涩的表达式来使读者感到迷惑.
1.
21内置的数学函数shell提供了对标准数学函数的访问.
这些函数通过使用C函数调用语法来调用.
表1-6列出了一组shell函数.
表1-6内置的shell函数名称函数abs绝对值log自然对数acos反余弦sin正弦asin反正弦sinh双曲线正弦cos余弦sqrt平方根cosh双曲线余弦tan正切exp指数函数tanh双曲线正切函数int浮点数取整除了使用int函数来提取某个浮点数的整数部分之外,本书中的shell脚本没有使用任何内置的shell函数.
1.
22文件权限、suid和sgid程序在编写shell脚本后,必须记住设置文件权限来使之可执行(executable).
可以使用chmod命令来修改文件的操作模式.
除了使脚本可执行之外,还可以将文件的操作模式修改为总是作为某个特定的用户(suid),或总是作为某个特定的系统组成员来执行(sgid),这称为设置粘滞位(stickybit).
如果试图对某个shell脚本使用suid或sgid命令,就会因为安全方面的原因而忽略这些命令.
设置某个程序总是作为某个特定用户或某个特定组的成员执行,这种方式通常用于使所有的用户或某一些用户在适当的环境下运行某个程序.
例如,大多数系统检查程序需要作为管理员用户运行,有时作为root运行.
我们不希望把密码告诉其他用户,因此可以使程序总是作为root运行,这样可以省去许多麻烦.
可以使用表1-7中列出的选项来设置文件权限,此外,读者也要注意参考chmod命令的参考页面,manchmod.
表1-7chmod权限选项权限值含义4000设置执行的用户ID2000设置执行的组ID1000设置目录的连接权限或设置文件的文本保存属性0400允许所有者读取0200允许所有者写入0100允许所有者执行或搜索0040允许组读取0020允许组写入0010允许组执行或搜索0004允许其他人读取0002允许其他人写入0001允许其他人执行或搜索通过使用chmod命令选项的组合,可以根据需要来设置针对某个文件或目录的操作权限.
记住,将某个shell脚本设置为suid或sgid将会被系统忽略.
chmod命令文件的权限值可通过十进制或r、w、x等记号表示.
不同方式可生成相同的结果.
1.
使某个脚本可执行chmod754my_script.
sh或chmodu+rwx,g+rx,o+rmy_script.
ksh所有者可以进行读取、写入和执行操作,组可以进行读取和执行操作,其他人可以进行读取操作.
2.
设置某个程序总是作为所有者而执行chmod4755my_program程序将总是作为文件的所有者而执行,只要该文件不是shell脚本就行.
文件的所有者可以进行读取、写入和执行操作,组可以进行读取和执行操作,其他人可以进行读取和执行操作.
因此,不管谁执行该文件,该文件总是像其所有者在执行一样.

3.
设置某个程序总是作为文件所有者的组成员而执行chmod2755my_program程序将总是作为文件所有者的组成员而执行,只要该文件不是shell脚本就行.
文件的所有者可以进行读取、写入和执行操作,组可以进行读取和执行操作,其他人可以进行读取和执行操作.
因此,不管谁执行该文件,该文件总是能够作为文件所有者的组成员而执行.

4.
设置某个程序总是作为文件所有者和文件所有者的组成员而执行chmod6755my_program程序将总是作为文件所有者以及文件所有者的组成员而执行,只要该文件不是shell脚本就行.
文件的所有者可以进行读取、写入和执行操作,组可以进行读取和执行操作,其他人可以进行读取和执行操作.
因此,不管谁执行该文件,该文件总是能够作为文件所有者以及文件所有者的组成员而执行.

1.
23在远程主机上运行命令有时想在某个远程主机上执行命令并在本地显示结果.
一个例子是收集来自一组计算机的文件系统统计数字.
可以使用rsh命令来完成这一任务,其命令语法为rshhostnamecommand_to_execute.
这个命令是一个很方便的小工具,但是在rsh命令能够工作之前,需要在所有主机上设置两个系统文件.
这两个文件是.
rhosts(该文件将在用户的宿主目录下创建,其文件权限为600,即只允许所有者读写文件)和/etc/hosts.
equiv文件.

出于安全考虑,在默认情况下,.
rhosts和hosts.
equiv文件不允许由某个远程shell执行.
一定要注意,这样会威胁到系统的安全.
有关设置的细节可以参考操作系统的文档.
提到安全,一种更好的解决方案是使用OpenSecureShell(OpenSSH)而不是rsh.
OpenSSH是一个加密的自由软件,在很多时候可以替代rsh、telnet、ftp.
要在使用了OpenSSH的另一台计算机上执行某个命令,可以使用以下命令语法.

sshuser@hostnamecommand_to_execute或者ssh-luserhostnamecommand_to_execute如果在两台计算机上都没有设置加密密钥对,该命令会提示用户输入一个密码.
设置密钥对(keypair)通常需花几分钟的时间.
也可以使用程序列表1-4和程序列表1-5给出的keyit脚本设置密钥,在ssh参考页面(manssh)中说明了设置的细节.
OpenSSH代码可以从www.
openssh.
org站点下载.
keyit.
dsa脚本如程序列表1-4所示,如果DSA密钥存在,则该脚本将设置DSA密钥.
程序列表1-4设置DSASSH密钥的keyit.
dsa脚本#!
/bin/Bash##SCRIPT:keyit.
dsa#PURPOSE:ThisscriptisusedtosetupDSASSHkeys.
Thisscriptmust#beexecutedbytheuserwhoneedsthekeyssetup.
REM_HOST=$1cat$HOME/.
ssh/id_dsa.
pub|ssh$REM_HOST"cat>>~/.
ssh/authorized_keys"keyit.
rsa脚本如程序列表1-5所示,如果RSA密钥存在,则该脚本将设置RSA密钥.
程序列表1-5设置RSASSH密钥的keyit.
rsa脚本#!
/bin/Bash##SCRIPT:keyit.
rsa#PURPOSE:ThisscriptisusedtosetupRSASSHkeys.
Thisscriptmustbeexecutedbytheuserwhoneedsthekeyssetup.
REM_HOST=$1cat$HOME/.
ssh/id_rsa.
pub|ssh$REM_HOST"cat>>~/.
ssh/authorized_keys"如果需要为新用户设置加密密钥,首先要对用户ID执行su命令,然后输入下面命令的其中之一.
若设置DSA密钥,输入以下命令.
ssh-keygen-tdsa若设置RSA密钥,输入以下命令.
ssh-keygen-trsa参见ssh-keygen的参考页面,那里有更加详细的说明:manssh-keygen.
1.
24设置陷阱若某个程序在其正常结束之前被终止,通常情况下可以捕获到一个退出信号.
该退出信号称为一个陷阱(trap).
表1-8列出了一些退出信号.
表1-8退出信号0-正常终止,脚本结束1SIGHUP挂起,线路断开2SIGINT终端中断,通常是Ctrl+C3SIGQUIT退出键,子进程在终止前死掉9SIGKILLkill-9命令,不能捕获这种退出状态15SIGTERMkill命令的默认操作19SIGSTOP停止,常为Ctrl+Z为查看操作系统的所有退出信号,可以输入以下命令.
#kill-l[That'skill-(ell)]这是一个很好的工具,可以在shell脚本中使用.
在捕获到退出信号时,可以在退出shell脚本之前执行一些清理命令.
捕获到退出信号时可以执行命令.
如果在shell脚本中加入了以下命令语句,将在屏幕上输出"EXITINGonaTRAPPEDSIGNAL",然后在遇到退出信号1、2、3和15时先清理现场,然后退出,注意不能捕获kill-9退出信号.
trap'echo"\nEXITINGonaTRAPPEDSIGNAL";exit'12315可以在退出之前添加需要清理的各种命令.
例如,可能需要在退出之前删除一些由shell脚本创建的文件.
1.
25用户信息命令有时需要向系统查询有关系统用户的某些信息.
1.
25.
1who命令who命令可以给每个登录用户提供以下信息:用户名、tty、登录时间以及用户的登录地点.
rmichaelpts/0Mar1310:24192.
168.
1.
104rootpts/1Mar1510:43(yogi)1.
25.
2w命令w命令实际上是一个扩展的who命令,输出类似下面的内容.
12:29PMup27days,21:53,2users,loadaverage1.
03,1.
17,1.
09Userttylogin@idleJCPUPCPUwhatrmichaelpts/0Mon10AM03:001wrootpts/110:42AM375:125:12tar注意,以上输出的最上面一行和uptime命令的输出结果一样.
w命令比who命令列出了更详细的输出信息,其中包括作业进程时间、总用户进程时间,但是没有给出用户的登录地点,而我们通常会因为安全的原因而在意这一信息.
w命令输出结果的一个好处是能够列出输入该命令时用户在干什么,这一信息十分有用.

1.
25.
3last命令last命令可以显示自从wtmp文件创建以来曾经登录到系统的用户名单.
如果需要对登录到系统的用户及其登录时间进行调查,则该命令是一个很好的工具.
下面是该命令的一个示例输出.
rootftpboobooAug0619:22-19:23(00:01)rootpts/3mrrangerAug0618:45stillloggedin.
rootpts/2mrrangerAug0618:45stillloggedin.
rootpts/1mrrangerAug0618:44stillloggedin.
rootpts/0mrrangerAug0618:44stillloggedin.
rootpts/0mrrangerAug0618:43-18:44(00:01)rootftpboobooAug0618:19-18:20(00:00)rootftpboobooAug0618:18-18:18(00:00)roottty0Aug0618:06stillloggedin.
roottty0Aug0212:24-17:59(4+05:34)rebootAug0212:00shutdowntty0Jul3123:23rootftpboobooJul3121:19-21:19(00:00)rootftpbambamJul3121:19-21:19(00:00)rootftpboobooJul3120:42-20:42(00:00)rootftpbambamJul3120:41-20:42(00:00)last命令的输出显示了用户名、登录端口、用户的登录地点、登录/注销的时间,以及登录会话的持续时间.
1.
26ps命令ps命令可以显示当前系统进程的信息.
ps命令具有许多可以改变显示内容的开关项.
表1-9列出了一些常见的命令选项.
表1-9常用ps命令选项ps命令选项含义ps用户当前运行的进程ps-f用户当前运行进程的完整列表ps-ef除内核进程外所有进程的完整列表ps-A所有进程,包括内核进程ps-Kf内核进程的完整列表psauxw根据CPU的使用时间%CPU排序后显示的进程列表1.
27与用户通信与系统用户进行通信可以让他们知道所发生的事情.
所有系统管理员都有维护窗口(maintenancewindow),在这个维护窗口中可以控制和处理一些脱机任务.
这仅仅是需要和系统用户(如果用户仍然处于登录状态的话)进行通信的一个例子.
使系统用户获得信息的最常用的方式是使用/ect/motd文件,该文件在每次用户登录时显示.
如果用户数天一直保持登录状态,则他看不到当前的任何新信息.
这正是需要实时通信的原因之一.
表1-10中显示的命令允许系统与当前登录到系统中的用户进行通信.

表1-10实时用户通信的命令命令含义wall在本地机上所有登录用户的屏幕上写入一条信息rwall在远程机上所有登录用户的屏幕上写入一条信息write写入一条信息给某个独立用户.
用户必须是登录用户talk启动一个交互式程序,允许两个用户进行对话.
屏幕被分隔成两部分,用户双方都可以看到对方输入的内容注意:当使用这些命令时,注意如果某个用户正在使用一个程序,例如一个报表统计软件,而且终端屏幕由该软件的界面所占据,则用户可能收不到信息,或者用户所在的屏幕可能会显得很混乱.
1.
28统一大写或小写文本以方便测试我们通常需要对诸如文件名、变量、文件文本等文本字符串来进行比较.
为了便于比较,应当将这些文本统一为大写形式或小写形式.
tr和typeset命令可用于将文本大写或小写.
这使得对变量输入内容之类的测试变得容易起来.
下面是使用tr命令的几个例子.

(1)变量值期望输入:TRUE实际输入:TRUE可能的输入:trueTRUETrue等(2)大写UPCASEVAR=$(echo$VARIABLE|tr'[a-z]''[A-Z]')(3)小写DOWNCASEVAR=$(echo$VARIABLE|tr'[A-Z]''[a-z]')在tr命令的以上例子中,对字符串使用了echo命令,也使用了一个管道符(|)来将echo语句的输出发送到tr命令.
如以上例子所示,大写时使用'[a-z]''[A-Z]'.
注意:需要使用单引号将方括号括起来.
'[a-z]''[A-Z]'用于将小写转为大写'[A-Z]''[a-z]'用于将大写转为小写这样,无论用户输入什么内容,总是可以得到稳定的输入:TRUE(如果采用大写)或true(如果采用小写).
这就减少了代码测试,而且也有利于提高脚本的可读性.
也可以使用typeset命令来控制某个变量在shell中的属性.
在前面的例子中,使用了变量VARIABLE.
可以通过设置属性来将所有的字符转换为大写或小写.
为将VARIABLE的大小写属性设置为总是转换为大写,可以使用以下命令.

typeset-uVARIABLEtypeset命令的-u开关项用于大写.
在使用typeset命令设置了VARIABLE变量的属性之后,每次为VARIABLE变量赋值文本字符串时,都自动转换为大写字符.
例如:typeset-uVARIABLEVARIABLE="True"echo$VARIABLETRUE为将VARIABLE的大小写属性设置为总是转换为小写,可以使用以下命令.
typeset-1VARIABLE例如:typeset-1VARIABLEVARIABLE="True"echo$VARIABLEtrue1.
29检测返回代码运行任何命令时,系统都会返回一个有关前一条被执行命令的响应,称为返回代码(returncode).
如果命令成功执行,则返回代码为0.
如果没有成功执行,则返回代码将是一个除0以外的值.
为了检测返回代码,可以查看$shell变量.
例如,想检查/usr/local/bin目录是否存在,以下每个代码块都可以完成这一任务.
test-d/usr/local/binif["$"-eq0]#Checkthereturncodethen#Thereturncodeiszeroecho'/usr/local/bindoesexist'else#ThereturncodeisNOTzeroecho'/usr/local/bindoesNOTexist'fi或iftest-d/usr/local/binthen#Thereturncodeiszeroecho'/usr/local/bindoesexist'else#ThereturncodeisNOTzeroecho'/usr/local/bindoesNOTexist'fi或if[-d/usr/local/bin]then#Thereturncodeiszeroecho'/usr/local/bindoesexist'else#ThereturncodeisNOTzeroecho'/usr/local/bindoesNOTexist'fi注意,一个例子使用$来检查返回代码,其他的例子使用了控制结构的内置测试.
内置测试可以完成与处理返回代码相同的任务,但是在过程中隐藏了这一步骤.
上面的3个例子给出了相同的结果,具体的选择可以根据个人的喜好和可读性要求来确定.

1.
30基于时间的脚本执行可能需要编写许多希望定时运行或在某个特定时间段运行的脚本.
本节使用一些例子来说明这些需求.
1.
30.
1cron表cron表(crontable)是一个系统文件,系统每分钟读取一次,而且将执行安排在该时间段执行的所有条目.
在默认情况下,任何用户都可以使用crontab-e命令来创建一个cron表,但是系统管理员可以通过cron.
allow和cron.
deny文件来控制允许哪些用户创建和编辑cron表.
当用户创建了自己的cron表,就可以在这个用户环境中运行命令、程序或脚本.
这就像是在执行命令前运行用户的$HOME/.
profile.

crontab-e命令在用户的cron表上启动默认的文本编辑器vi或emacs.
注意:当使用crontab命令时,cron表作用的对象是当前的用户ID.
为列出当前用户的cron表内容,可以使用crontab-1命令.
知道cron表条目中的每个字段都是干什么的很重要.
下面的cron表条目可以在1月8日所在星期的每天早晨3:15执行脚本/usr/local/bin/somescript.
ksh.
注意在weekday字段中使用了一个通配符.

MinuteoftheHour(0-59)HouroftheDay(0-23)DayoftheMonth(1-31)MonthoftheYear(1-12)DayoftheWeek(0-6forSundaythroughSaturday)CommandtoExecute(Fullpathisrequired)15381*/usr/local/bin/somescript.
Bash2>&1>/dev/null下面是另一个crontable的例子.
0011*/usr/bin/banner"HappyNewYear">/dev/console在1月1日午夜00:00,该cron表条目以大标题形式在系统的控制台(/dev/console)上写入HappyNewYear.
通配符*匹配任何数目的字符.
匹配单个字符.
1.
30.
2at命令和cron表一样,at命令也可以根据时间来执行命令.
通过使用at命令,可以安排某个作业在特定时间运行一次.
当作业执行时,at命令将发送一个关于标准输出和标准错误的电子邮件给安排该作业运行的用户(除非输出被重定向).
作为系统管理员,可以通过at.
allow和at.
deny文件来控制允许哪些用户安排作业.
在修改这些文件之前,可以参考具体操作系统的参考页面,同时了解多种使用at命令的方式,来进行基于时间控制的命令执行.
为在10分钟后执行一个命令,可以使用下面的语法.

echo'/usr/local/bin/somescript.
Bash'|atnow+10minutes也可以通过下面的语法交互地使用at命令.
atnow+10minutesEnter然后输出所有要执行的命令的路径名,按Enter键,最后按Ctrl+D键.
下面是一个例子.
atnow+10minutes/usr/local/bin/somescript.
BashCtrl+D要了解更多的at命令选项,参见at命令参考页面manat.
1.
31输出控制脚本是如何运行的输出到哪里了这些问题可以归结为作业控制.
1.
31.
1静默运行要以静默方式(silentmode)来执行一个脚本,可以使用以下语法.
/PATH/script_name2>&1>/dev/null在上面的命令语句中,script_nameshell脚本执行时不会输出任何内容到屏幕上.
之所以这样,是因为命令用以下语句终止.
2>&1>/dev/null通过以上方式终止一条命令,可以将由文件描述符2指定的标准错误(stderr)重定向到文件描述符1指定的标准输出(stdout).
然后,使用另一个到/dev/null的重定向,将所有的输出发送到位桶(bitbucket).
可以将以上方式称为静默运行(silentrunning),这意味着从脚本中绝对不会输出任何内容到屏幕上.
在脚本内,可能有一些输出被定向到文件或设备(某个特定的终端),或者是系统控制台,/dev/console,但是没有任何输出到屏幕.
这种方式在执行来自系统cron表的某个脚本时特别有用.

在下面示例的cron表条目中,希望执行一个名为/usr/local/bin/systemcheck.
ksh的脚本,该脚本需要作为root用户运行,而且是每周7天、每天24小时、每15分钟就运行一次,同时不在屏幕上输出任何内容.
由于使用了下面的语句结束cron表条目,因此不会有任何屏幕输出.

2>&1>/dev/null在脚本内,可能会进行某种类型的通告,例如向员工发出传呼或发送输出给系统的控制台,写入某个文件或磁带设备,但是诸如echo"HelloWorld"命令语句将输出到位桶,而shell脚本中的echo"HelloWorld">/de/console命令语句,将输出到系统定义的控制台.
该cron表条目需要使用以下语法来设置到rootcron表中(必须作为root用户登录).
5,20,35,50usr/local/bin/systemcheck.
ksh2>&1>/dev/null注意:大多数系统的检查类型的脚本需要放置在rootcron表中.
当然,要编辑root的cron表,用户必须作为root登录.
以上cron表条目将每周7天、每天24小时、每小时的第5、20、35、50分钟(即每15分钟)执行一次/usr/local/bin/systemcheck.
ksh脚本.
由于2>&1>/dev/null的存在,该脚本不会在屏幕上产生任何输出.
当然,具体执行脚本的时间可以是任意时刻.
有时希望在cron表中均匀分布执行时间,这样就不会同时执行许多占用CPU时间的脚本和程序.

1.
31.
2使用getopts解析命令行参数getopts命令内置于shell中,可以获取由单个字符所指定的有效命令行参数,单个字符前有一个减号-或加号+.
为指定某个命令行开关项需要一个开关参数,可以在开关项后加一个冒号(:).
如果开关项不需要任何参数,则冒号可省略.
所有这些选项集中在一起称为OptionString,后面跟随一些变量名.
每个开关项的参数保存在一个称为OPTARG的变量中.
如果整个OptionString前面有一个冒号,则任何未匹配的开关项将会导致在VARIABLE中加载一个号,命令的形式如下.

getoptsOptionStringVARIABLE[Argument.
.
.
]解释这个命令最容易的方式是使用一个例子.
对于这些需要花时间和进程去监视的脚本,可以为它们提供一个参数,即-s5-m10-pmy_backup.
在该参数中指定了5秒、10分钟,且进程为my_backup.
注意,在开关项和参数之间不必存在空格,这正是getopts的优点所在.
以下命令行可用于设置这个例子.
SECS=0#InitializealltozeroMINUTES=0HOURS=0DAYS=0PROCESS=Initializetonullwhilegetopts:s:m:h:d:p:TIMED2>/dev/nulldocase$TIMEDins)SECS=$OPTARG;;m)((MINUTES=$OPTARG*60));;h)((HOURS=$OPTARG*3600));;d)((DAYS=$OPTARG*86400));;p)PROCESS=$OPTARG;;\)usageexit1;;esacdone((TOTAL_SECONDS=SECONDS+MINUTES+HOURS+DAYS))在getopts命令中有几点需要注意,对于这个例子来说,getopts命令需要成为while循环的一部分(在该循环中有一个case语句).
对于指定的每个选项s、m、h、d和p,在每个开关项后加入了一个冒号.
这就告诉getopts这些开关项需要一个参数.
在OptionString列表之前的冒号要求getopts在给定一个未详细指明的选项时,将TIMED变量设置为"".
这允许使用一个返回代码1来调用usage函数和exit函数.
要注意的一点是,getopts不管接收参数,因此如果希望退出,则必须手动进行.
最后要注意的一点是,while循环的第一行将标准错误(文件描述符2)重定向到位桶.
在遇到一个未详细指明的参数时,getopts将发送一条消息给标准错误(文件描述符2).
因为我们期望这种情况出现,所以只需忽略这些消息并将它们丢弃到/dev/null即可.
本书将讨论许多有关getopts的内容.

1.
31.
3使用后台函数创建一个协作进程还需要讨论协作进程(co-process)的设置问题,协作进程是在某个前台进程和后台进程之间的通信连接.
最普遍的问题是为什么需要协作进程.
在某个脚本中需要调用一个函数,当我们对主脚本进行定时控制时该函数完成所有的进程监视工作.
这时候问题就出现了,因为需要在后台运行这个函数,而且这个函数有一个无限循环.
由于不能通知循环终止,该循环将在主脚本以及函数终止后一直运行,这就会导致一个或多个死进程.
因此,在主脚本中,需要一种方式来与该循环进行通信,这样后台函数在递减计数结束或者是脚本使用Ctrl+C键终止后,能够通知循环终止并很快退出函数.
为解决这个小问题,将proc_watch函数作为一个后台协作进程.
也许有人会问,如何来完成这一点"将其通过管道输送到后台"是最简单的方式,看起来似乎也正是这样.
来看下面的代码段,如程序列表1-6所示.

程序列表1-6使用协作进程的示例代码functiontrap_exit{#Telltheco-processtobreakoutoftheloopBREAK_OUT='Y'print-p$BREAK_OUT#Use"print-p"totalktotheco-process}functionproc_watch{#Thisfunctionisstartedasaco-process!
!
!
whileLoopforeverdoSomeCodeHereread$BREAK_OUT#DoNOTneeda"-p"toread!
if[[$BREAK_OUT='Y']]thenreturn0fidone}#####StartofMain###########SetaTrap###trap'trap_exit;exit2'12315TOTAL_SECONDS=300BREAK_OUT='N'proc_watchStartproc_watchasaco-process!
!
!
!
PW_PID=$1#ProcessIDofthelastbackgroundjobuntil((TOTAL_SECONDS==0))do((TOTAL_SECONDs=TOTAL_SECONDS-1))sleep1doneBREAK_OUT='Y'#Use"print-p"tocommunicatewiththeco-processvariableprint-p$BREAK_OUTkill$PW_PID#Killthebackgroundco-processexit0在以上代码段中定义了两个函数.
trap_exit函数将在捕获到退出信号1、2、3、15时执行,另一个函数为proc_watch函数,是一个希望作为后台进程而启动的函数.
在proc_watch函数中可以看到,它具有一个无限循环,如果主脚本终止,则缺乏一种手段来使函数中的循环退出,其中的循环将一直执行下去.
为解决这个问题,使用管道符号|与和号&作为后缀,以"将其通过管道输入到后台"的方式将proc_watch作为一个协作进程启动.
当希望与这个作为协作进程的后台函数通信时,可以使用print-p$VARIABLE_NAME.
在协作进程函数中,只使用标准的read$VARIABLE_NAME.
我们将使用这种机制在主脚本捕获到退出信号而终止的情况下中断循环,当然,无法使用陷阱来捕获一个kill-9.
可以尝试用一个具有无限循环的后台函数来设置以上描述的情形,然后按Ctrl+C键来杀死主脚本并执行ps-ef|more,将看到后台循环仍然在执行!
获得PID并针对该PID执行一个kill-9来杀死它.
当然,如果循环的退出条件已经满足,将自行退出.
1.
32捕获延迟命令输出你是否遇到过捕获延迟命令输出的困境呢当你希望获得命令输出却未捕获到时,会感到特别沮丧.
有一些技术可以帮助捕获这些延迟的输出响应,窍门在于使用一个until循环.
看看下面的代码.
OUTFILE="/tmp/outfile.
out"#Definetheoutputfilecat/dev/null>$OUTFILE#Createazerosizeoutputfile#Startanuntillooptocatchthedelayedresponseuntil[-s$OUTFILE]dodelayed_output_command>>$OUTFILEdone#Showtheresultingoutputmore$OUTFILE以上代码段首先定义了一个输出文件来保存延迟的输出数据.
以一个零字节大小的文件作为开始,然后进入一个until循环,直到$OUTFILE不再是一个零字节文件才退出until循环.
最后一步是向用户显示从延迟输出捕获的数据.

1.
33逐行处理文件的最快方式大多数shell脚本需要和文件一起工作,而且其中一些脚本使用文件来作为数据输入.
本节展示了几种第2章"逐行处理文件的24种方法"所研究的用于逐行处理文件的最快方法.
这两种技术如程序列表1-7和程序列表1-8所示.

程序列表1-7方法12并列第一名functionwhile_read_LINE_bottom_FD_OUT{#Zerooutthe$OUTFILE>$OUTFILE#Associatestandardoutputwithfiledescriptor4#andredirectstandardoutputto$OUTFILEexec4$OUTFILEwhilereadLINEdoecho"$LINE":done&-}程序列表1-7中描述的方法处理文件的时间最短.
这种方法通过在循环底部,done循环终止符后重定向文件输入,这是我所喜爱的进行文件输入重定向的方法.
这种方法为重定向标准输出stdout使用了一个文件描述符1.

使用done$OUTFILE#Associatestandardinputwithfiledescriptor3#andredirectstandardinputto$INFILEexec3$OUTFILEwhilereadLINEdoecho"$LINE":done#Restorestandardoutputandclosefile#descriptor4exec1&-#Restorestandardinputandclosefile#descriptor3exec0&-}当我进行shell脚本编写时,并不喜欢使用这种方法,因为在整个代码生命周期中的维护是很困难的.
如果使用者对文件描述符并不熟悉,那么使用这种方法的脚本是很难理解的.
程序列表1-7的方法产生了相同的计时结果,而且非常容易理解.
程序列表1-9所示的方法处理时间排名第二.

程序列表1-9方法16计时测试中的第二名for_LINE_cat_FILE_cmdsub2_FD_OUT(){#Method16#Zerooutthe$OUTFILE>$OUTFILE#Associatestandardoutputwithfiledescriptor4#andredirectstandardoutputto$OUTFILEexec4$OUTFILEforLINEin$(cat$INFILE)doecho"$LINE":done#Restorestandardoutputandclosefile#descriptor4exec1&-}程序列表1-10中的方法会给我们另外一个惊喜:使用文件描述符进行命令替换的for循环,对文件输出进行了重定向.
程序列表1-10方法15计时测试中的第三名for_LINE_cat_FILE_FD_OUT(){#Method15#Zerooutthe$OUTFILE>$OUTFILE#Associatestandardoutputwithfiledescriptor4#andredirectstandardoutputto$OUTFILEexec4$OUTFILEforLINEin`cat$INFILE`doecho"$LINE":done#Restorestandardoutputandclosefile#descriptor4exec1&-}程序列表1-11中的方法是不用文件描述符的情况下,逐行处理文件最快的方法.
程序列表1-11不使用文件描述符情况下速度最快的方法functionwhile_read_LINE_bottom{#Method2#Zerooutthe$OUTFILE>$OUTFILEwhilereadLINEdoecho"$LINE">>$OUTFILE:done=60&&SEC3600))&&echo"[Elapsedtime:$((SEC/3600))hr\$(((SEC%3600)/60))min$(((SEC%3600)%60))sec]\c"}一个使用elapsed_time函数的例子如下.
elapsed_time6465[Elapsedtime:1hr47min45sec]注意,elapsed_time函数一直在相同的文本行执行,而不会产生新行.
为了添加新行,可以将3个echo命令末尾的\c变成\n.
1.
37使用记录文件我们经常需要给记录文件里的每一个记录添加文件名.
程序列表1-18的代码说明了通过对一系列记录文件名进行循环处理的一个合并过程.
像添加记录文件建立大的批处理文件一样,给每一个记录添加文件名.
阅读程序列表1-18,然后会有详细的解释.

程序列表1-18固定长度记录文件合并的进程代码MERGERECORDFILE=/data/mergerecord.
$(date+%m%d%y)RECORDFILELIST=/data/branch_records.
lstwhilereadRECORDFILENAMEdoseds/$/$(basename$RECORDFILENAME)/g\$RECORDFILENAME>>$MERGERECORDFILEdone>$MERGERECORDFILEdone/dev/null\|od-tu4|awk'{print$2}'|head-n1)注意,程序列表1-20中的dd命令使用/dev/random作为输入文件,使得count等于1,就可以返回数据的一个字节.
然后,通过文件描述符2的限定,把所有的标准错误输出给位桶,这一点非常重要.
如果省略了2>/dev/null重定向,可能会得到意想不到的数据.
剩余的标准输出可以用管道输入给od命令将二进制数据转换成一个无符号整数,也可以使用-tu4命令选项.
通过改变赋给u的值,可以改变返回的随机数的长度.
为产生一个64位,而不是64字符的随机数,可以将-tu4变成-tu8.
第11章"伪随机数和数据的生成"里有更多关于这方面的例子.
1.
39检查AlX中的失效磁盘分区理想情况下,希望失效磁盘分区值为0.
如果该值大于0,则表示有问题.
特别地,在这个逻辑卷(LogicalVolume)中的镜像磁盘不会进入同步状态,这将导致一个毫无价值的镜像.
来看下面的命令语句.
LV=apps_lvNUM_STALE_PP=$(lslv-L$LV|grep"STALEPP"|awk'{print$3}'上面的语句将失效磁盘分区的数目保存到NUM_STALE_PP变量中,是通过VARIABLE=$(commands)指定的命令替换来完成这一任务的.
1.
40自动主机ping根据所用操作系统的不同,ping命令的用法也有所不同(如果希望发送3个ping测试包到每个主机来看看这些计算机是否在运行的话).
程序列表1-21所示的函数可以从AIX、HP-UX、Linux和SunOS计算机发送ping命令.

程序列表1-21ping_host函数functionping_host{HOST=$1#GrabthehosttopingfromARG1.
PING_COUNT=3PACKET_SIZE=54#Thisnextcasestatementexecutesthecorrectping#commandbasedontheUnixflavorcase$(uname)inAIX|OpenBSD|Linux)ping-c${PING_COUNT}$HOST2>/dev/null;;HP-UX)ping$HOST$PACKET_SIZE$PING_COUNT2>/dev/null;;SunOS)ping-s$HOST$PACKET_SIZE$PING_COUNT2>/dev/null;;*)echo"\nERROR:UnsupportedOperatingSystem-$(uname)"echo"\n\t.
.
.
EXITING.
.
.
\n"exit1esac}shell脚本的主体必须提供要进行ping测试的主机名,这通常通过一个loop循环来完成.
1.
41高亮显示文件中的特定文本本节说明的技术可以在显示整个文件的同时,以反白显示的方式高亮显示文件中的特定文本.
为了加入反白显示代码段,必须使用tput命令在sed语句中进行一些命令替换.
在指定new_string的地方,将使用命令替换来加入针对反白显示的控制,一个用于打开高亮显示,一个用于关闭高亮显示.
在加入了命令替换后,sed语句将类似如下所示.

seds/string/$(tputsmso)string$(tputrmso)/g也希望字符串被赋值到某个变量,如下所示.
seds/"$STRING"/$(tputsmso)"$STRING"$(tputrmso)/g注意,字符串变量使用了双引号将它括起,"$STRING",不要忘记在变量上加上双引号.
作为使用命令替换的一种体验,读者可以在任何UNIX计算机上尝试用以下命令来高亮显示/etc/hosts文件中的计算机主机名.
cat/etc/hosts|seds/'hostname'/$(tputsmso)'hostname'$(tputrmso)/g1.
42使打印机一直打印在大企业中,打印机经常需要一直保持运转.
有两种技术可以使打印机一直进行打印工作.
一种技术是针对AIX的"传统"打印机子系统,另一种是针对SystemV和CUPS打印的.
1.
42.
1AIX的"传统"打印机子系统为使AIX的"传统"打印机子系统的打印队列运行,可以使用以下命令之一.
enable$(enq-AW|tail+3|grepDOWN|awk'{print$1}')2>/dev/null或enable$(lpstat-W|tail+3|grepDOWN|awk'{print$1}')2>/dev/null1.
42.
2SystemV和CUPS打印为使SystemV和CUPS打印机一直打印,可以使用以下命令之一.
lpcenable$(lpstat-a|grep'notaccepting'|awk'{print$1}')lpcstart$(lpstat-p|grepdisabled|awk'{print$2}')lpcupall#Enableallprintingandqueuing使用rootcron表以每隔15分钟左右的间隔执行相应的命令是一个不错的主意.
1.
43自动进行FTP文件传输可以使用一个here文档来对FTP文件传输进行脚本自动化处理,基本处理如下.
ftp-i-v-nwilma$MEG_BYTESstartingatthe$SEARCH_PATH#HOLD_FILE=/tmp/largefiles.
listMEG_BYTES=$1SEARCH_PATH=$(pwd)#Usethecurrentdirectoryfind$SEARCH_PATH-typef-size+${MEG_BYTES}000000c-print>$HOLDFILE注意,在find命令的-size参数后面,文件尺寸之前有一个加号(+),而且加入了一个c作为后缀.
这一组合指定了大于$MEG_BYTES的文件(这里用字节而不是用块来测量文件的大小).

1.
46捕获用户的按键操作在大多数大公司中,有时存在监视用户操作的需求,或者甚至希望审核那些使用root账户或其他管理账户(例如oracle)来访问系统的用户进行的按键操作.
另外,来自公司承包方的外单位人员在现场作业时也会带来某些安全风险.
通常,当一个新应用在公司环境中投入使用时,会有一个或两个合作方在现场待一段时间,进行产品安装、故障排除以及用户培训等工作.

程序列表1-23所示的代码使用了script命令来捕获所有的按键操作.
程序列表1-23捕获一个用户的按键操作TS=$(date+%m%d%y%H%M%S)FiletimestampTHISHOST=$(hostname|cut-f1-2-d.
)#HostnameofthismachineLOGDIR=/usr/local/logs/script#DirectorytoholdthelogsLOGFILE=${THISHOST}.
${LOGNAME}.
$TS#Createsthenameofthelogfiletouch$LOGDIR/$LOGFILE#Createstheactualfile#SetthecommandpromptexportPS1="[$LOGNAME:$THISHOST]@"'$PWD>'RUNITHEREchown$LOGNAME${LOGDIR}/${LOGFILE}#Lettheuserownthelogfile#whilethescriptexecuteschmod600${LOGDIR}/${LOGFILE}#ChangepermissiontoRWfortheownerscript${LOGDIR}/${LOGFILE}Startthescriptmonitoringsessionchownroot${LOGDIR}/${LOGFILE}#Changetheownershiptorootchmod400${LOGDIR}/${LOGFILE}#Setpermissiontoread-onlybyroot1.
47使用bc实用工具来进行浮点数学运算在UNIX计算机上有一个称为bc的实用工具,它是一个针对任意精度的算术语言的解释程序.
bc命令是一个交互式程序,提供了任意精度的算术运算.
在命令行中输入bc就可以启动一个bc交互式会话.
一旦进入到会话中,就可以输入大多数复杂的算术表达式来进行运算,就像使用计算器一样.

程序列表1-24显示的代码段创建了针对bc实用工具的数学表达式,然后使用一个here文档来将表达式加载到bc中.
程序列表1-24shell脚本里使用bc的例子#Loopthrougheachnumberandbuildamathstatementthat#willaddallofthenumberstogether.
forXin$NUM_LISTdoADD="$ADD$PLUS$X"PLUS="+"done#Dothemathherebyusingaheredocumenttosupply#inputtothebccommand.
Thesumofthenumbersis#assignedtotheSUMvariable.
SUM=$(bctypeset-i16BASE_16_NUM[root@yogi:/scripts]>BASE_16_NUM=8#472521[root@yogi:/scripts]>echo$BASE_16_NUM16#735c91.
48.
2使用printf命令可以通过printf命令将十进制数转换成八进制或十六进制.
Convertabase10numbertobase8#printf%o2039847656Convertabase10numbertobase16#printf%x203984fae使用下列printf命令可以以指数形式显示一个数.
#printf%e203982.
039800e+041.
49使用select命令创建菜单很多时候需要为终端用户提供一个进行选择的菜单,这正是select命令语句的用武之地.
在下面的代码中,菜单提示项被赋给PS3系统变量,而select命令语句的用法类似于一个for循环,此外,还使用了一个case语句来指定对于各种选择所采取的操作.
#!
/bin/Bash##SCRIPT:select_system_info_menu.
Bash#AUTHOR:RandyMichael#DATE:1/17/2008#REV:1.
0##PURPOSE:Thisshellscriptusestheshell'sselect#commandtocreateamenutoshowsysteminformation#Clearthescreenclear#Displaythemenutitleheaderecho-e"\n\tSYSTEMINFORMATIONMENU\n"#DefinethemenupromptPS3="SelectanoptionandpressEnter:"#Theselectcommanddefineswhatthemenu#willlooklikeselectiinOSHostFilesystemsDateUsersQuitdocase$iinOS)echouname;;Host)echohostname;;Filesystems)echodf-k|more;;Date)echodate;;Users)echowho;;Quit)break;;esac#Settingtheselectcommand'sREPLYvariable#toNULLcausesthemenutoberedisplayedREPLY=#Pausebeforeredisplayingthemenuecho-e"\nPressEntertoContinue.
.
.
\c"read#Readytoredisplaythemenuagain#clearthescreenclear#Displaythemenutitleheaderecho-e"\n\tSYSTEMINFORMATIONMENU\n"done#Clearthescreenbeforeexitingclear注意在以上代码段中select语句的用法,看起来有点像一个带有一组值的for循环.
接下来是一个嵌入的case语句,允许指定针对各种选择所采取的操作.
下面显示了这个简单菜单的输出结果.
SYSTEMINFORMATIONMENU1)OS3)Filesystems5)Users2)Host4)Date6)QuitSelectanoptionandpressEnter:1LinuxPressEntertoContinue.
.
.
1.
50删除文件中的重复行uniq命令用于报告并删除文件中的重复行.
这是一个可以进行各种脚本编程和测试的有用工具,其语法如下所示.
如果有一个带有重复行的文件my_list,希望将去掉重复行后的文件保存为my_list_no_repeats,可以使用以下命令.
#uniqmy_listmy_list_no_repeats如果希望查看不带重复行的文件的输出结果,可以使用以下命令.
#catrepeat_file|uniq1.
51删除文件中的空白行从文件中删除空白行的最简单的方法是使用一个sed命令语句,以下语法可以删除空白行.
#catmy_file|sed/^$/d或#sed/^$/dmy_file1.
52测试NULL变量没有赋值的变量有时很难处理,以下的测试可以确定某个变量是NULL变量还是一个已经赋值的变量.
这里的双引号非常重要,必须使用.
VAL=#CreatesaNULLvariableif[[-z"$VAL"&&"$VAL"thenecho"TheVALvariableisNULL"fi或VAL=25if[[!
-z"$VAL"&&"$VAL"thenecho"TheVALvariableisNOTNULL"fi1.
53直接访问上一个位置参数$#的值为直接访问$#位置参数的值,可以使用以下命令.
eval'$'$#或eval\$$#读者在本书后面将会看到,这一技术有多种用法.
1.
54删除命令输出中的列标题很多情况下希望删除某个命令输出中的列标题.
许多人试图使用grep-v模式来匹配标题中的某些内容,一种更简单、更可靠的方法是使用tail命令,以下是针对df命令输出的一个例子.
[root:yogi]@/scripts#df-kFilesystem1024-blocksFree%UsedIused%IusedMountedon/dev/hd4327681579652%192712%//dev/hd214663686256896%4480113%/usr/dev/hd9var53248811285%10278%/var/dev/hd31064966899636%2451%/tmp/dev/hd1409638925%556%/home/procproc/dev/hd10opt6553601642098%1626110%/opt/dev/scripts_lv1024002401277%11375%/scripts/dev/lv_temp40960014745265%291%/tmpfs现在来看看去除列标题后的同一输出结果.
[root:yogi]@/scripts#df-k|tail+2/dev/hd4327681579652%192712%//dev/hd214663686256896%4480113%/usr/dev/hd9var53248811285%10278%/var/dev/hd31064966899636%2451%/tmp/dev/hd1409638925%556%/home/procproc/dev/hd10opt6553601642098%1626110%/opt/dev/scripts_lv1024002401277%11375%/scripts/dev/lv_temp40960014745265%291%/tmpfs记住对希望删除的总行数加1.
1.
55数组shell支持一维数组.
数组元素的最大数目为1024.
当定义了一个数组后,将自动将其大小确定为1024个元素.
一维数组包含一系列数组元素,类似于链接成一列火车的火车车厢.
一维数组元素可以是除其他数组之外的任何内容.
读者可能会想到,可以使用一个数组来访问另一个数组以创建二维或三维数组,但这一内容超出了本书的讨论范围.

数组可以按照两种方式来加载.
第一种方式是使用set-A命令来定义并加载数组,一步到位.
第二种方式则是逐个加载数组元素.
本节介绍这两种技术.
set-AMY_ARRAYalphabetagamma或X=0#Initializecountertozero.
#Loadthearraywiththestringsalpha,beta,andgammaforELEMENTinalphagammabetadoMY_ARRAY[$X]=$ELEMENT((X=X+1))done第一个数组元素由0而不是1来引用.
为访问数组元素,可以使用以下语法.
echo${MY_ARRAY[2]#Showthethirdarrayelementgammaecho${MY_ARRAY[*]#Showallarrayelementsalphabetagammaecho${MY_ARRAY[Showallarrayelementsalphabetagammaecho${#MY_ARRAY[Showthetotalnumberofarrayelements3echo${#MY_ARRAY[Showthetotalnumberofarrayelements3echo${MY_ARRAY}Showarrayelement0(thefirstelement)alpha本书有很多章的shell脚本中都用到了数组.
1.
56测试字符串在shell脚本中要做的最困难的事情之一是测试来自命令行的用户输入.
程序列表1-25中的shell脚本可以完成这一任务,它通过使用正则表达式来确定字符串的组成部分.
程序列表1-25test_string.
kshshell脚本#!
/bin/ksh##SCRIPT:test_string.
ksh#AUTHOR:RandyMichael#REV:1.
0.
D-Usedfordevelopement#DATE:10/15/2007#PLATFORM:NotPlatformDependent##PURPOSE:Thisscriptisusedtotestacharacterstring,orvariable,foritscomposition.
Examplesincludenumeric,lowercaseoruppercasecharacters,alpha-numericcharacters,andIPaddress.
##REVLIST:###set-x#Uncommenttodebugthisscript#set-n#Uncommenttoverifysyntaxwithoutanyexecution.
REMEMBER:PutthecommentbackorthescriptwillNOTEXECUTE!
#DEFINEFUNCTIONSHEREtest_string(){#Thisfunctiontestsacharacterstring#Musthaveoneargument($1)if1))then#Thiserrorwouldbeaprogrammingerrorprint"ERROR:$(basename$0)requiresoneargument"return1fi#Assignarg1tothevariable-->STRINGSTRING=$1#Thisiswherethestringtestbeginscase$STRINGin+([0-9]).
+([0-9]).
+([0-9]).
+([0-9]))#TestingforanIPaddress-validandinvalidINVALID=FALSE#Separatetheintegerportionsofthe"IP"address#andtesttoensurethatnothingisgreaterthan255#oritisaninvalidIPaddress.
foriin$(echo$STRING|awk-F.
'{print$1,$2,$3,$4}')doif((i>255))thenINVALID=TRUEfidonecase$INVALIDinTRUE)print'INVALID_IP_ADDRESS';;FALSE)print'VALID_IP_ADDRESS';;esac;;+([0-1]))#Testingfor0-1onlyprint'BINARY_OR_POSITIVE_INTEGER';;+([0-7]))#Testingfor0-7onlyprint'OCTAL_OR_POSITIVE_INTEGER';;+([0-9]))#Checkforanintegerprint'INTEGER';;+([-0-9]))#Checkforanegativewholenumberprint'NEGATIVE_WHOLE_NUMBER';;+([0-9]|[.
][0-9]))#Checkforapositivefloatingpointnumberprint'POSITIVE_FLOATING_POINT';;+(+[0-9][.
][0-9]))#Checkforapositivefloatingpointnumber#witha+prefixprint'POSITIVE_FLOATING_POINT';;+(-[0-9][.
][0-9]))#Checkforanegativefloatingpointnumberprint'NEGATIVE_FLOATING_POINT';;+([-.
0-9]))#Checkforanegativefloatingpointnumberprint'NEGATIVE_FLOATING_POINT';;+([+.
0-9]))#Checkforapositivefloatingpointnumberprint'POSITIVE_FLOATING_POINT';;+([a-f]))#Testforhexidecimaloralllowercasecharactersprint'HEXIDECIMAL_OR_ALL_LOWERCASE';;+([a-f]|[0-9]))#Testforhexidecimaloralllowercasecharactersprint'HEXIDECIMAL_OR_ALL_LOWERCASE_ALPHANUMERIC';;+([A-F]))#Testforhexidecimaloralluppercasecharactersprint'HEXIDECIMAL_OR_ALL_UPPERCASE';;+([A-F]|[0-9]))#Testforhexidecimaloralluppercasecharactersprint'HEXIDECIMAL_OR_ALL_UPPERCASE_ALPHANUMERIC';;+([a-f]|[A-F]))#Testingforhexidecimalormixed-casecharactersprint'HEXIDECIMAL_OR_MIXED_CASE';;+([a-f]|[A-F]|[0-9]))#Testingforhexidecimal/alpha-numericstringsonlyprint'HEXIDECIMAL_OR_MIXED_CASE_ALPHANUMERIC';;+([a-z]|[A-Z]|[0-9]))#Testingforanyalpha-numericstringonlyprint'ALPHA-NUMERIC';;+([a-z]))#Testingforalllowercasecharactersonlyprint'ALL_LOWERCASE';;+([A-Z]))#Testingforalluppercasenumbersonlyprint'ALL_UPPERCASE';;+([a-z]|[A-Z]))#Testingformixedcasealphastringsonlyprint'MIXED_CASE';;*)#Noneofthetestsmatchedthestringcompositionprint'INVALID_STRING_COMPOSITION';;esac}usage(){echo"\nERROR:Pleasesupplyonecharacterstringorvariable\n"echo"USAGE:$THIS_SCRIPT{characterstringorvariable}\n"}BEGINNINGOFMAIN#Querythesystemforthenameofthisshellscript.
#Thisisusedforthe"usage"function.
THIS_SCRIPT=$(basename$0)#Checkforexactlyonecommand-lineargumentif1))thenusageexit1fi#Everythinglooksokayifwegothere.
Assignthe#singlecommand-lineargumenttothevariable"STRING"STRING=$1#Callthe"test_string"functiontotestthecomposition#ofthecharacterstringstoredinthe$STRINGvariable.
test_string$STRING#Endofscript这是一个好的开始,但是这个shell脚本并没有涵盖所有的内容.
读者可以试试这个脚本,看看能否对其进行一些改进.
注意:Bashshell不支持+([0-9])-类型的正则表达式.
1.
57小结本章是一些初级知识,主要目的是让读者可以进行快速浏览,同时了解一些技巧和窍门.
在接下来的27章中,将编写大量的shell脚本来解决实际问题.
请读者做好进入UNIX世界的准备吧!
接下来首先要学习的内容是24种逐行处理文件的方法.
在过去的15年里,我们了解到许多用于逐行处理文件的技术,有些技术相当具有创造性.
下一章将介绍我知道的最常见的24种文件处理技术,该章的最后是一个shell脚本,主要用于对各种技术进行计时以确定运行速度最快的技术.
读者可以仔细阅读并确定哪种技术胜出.
下一章见!

舍利云:海外云服务器,6核16G超大带宽vps;支持全球范围,原价516,折后价200元/月!

舍利云怎么样?舍利云推出了6核16G超大带宽316G高性能SSD和CPU,支持全球范围,原价516,折后价200元一月。原价80美元,现价30美元,支持地区:日本,新加坡,荷兰,法国,英国,澳大利亚,加拿大,韩国,美国纽约,美国硅谷,美国洛杉矶,美国亚特兰大,美国迈阿密州,美国西雅图,美国芝加哥,美国达拉斯。舍利云是vps云服务器的销售商家,其产品主要的特色是适合seo和建站,性价比方面非常不错,...

10gbiz七月活动首月半价$2.36/月: 香港/洛杉矶CN2 GIA VPS

10gbiz怎么样?10gbiz 美国万兆带宽供应商,主打美国直连大带宽,真实硬防。除美国外还提供线路非常优质的香港、日本等数据中心可供选择,全部机房均支持增加独立硬防。洛杉矶特色线路去程三网直连(电信、联通、移动)回程CN2 GIA优化,全天低延迟。中国大陆访问质量优秀,最多可增加至600G硬防。香港七星级网络,去程回程均为电信CN2 GIA+联通+移动,大陆访问相较其他香港GIA线路平均速度更...

TmhHost香港三网CN2 GIA月付45元起,美国CN2 GIA高防VPS季付99元起

TmhHost是一家国内正规公司,具备ISP\ICP等资质,主营国内外云服务器及独立服务器租用业务,目前,商家新上香港三网CN2 GIA线路VPS及国内镇江BGP高防云主机,其中香港三网CN2 GIA线路最低每月45元起;同时对美国洛杉矶CN2 GIA线路高防及普通VPS进行优惠促销,优惠后美国洛杉矶Cera机房CN2 GIA线路高防VPS季付99元起。香港CN2 GIA安畅机房,三网回程CN2 ...

chmod用法为你推荐
蒋存祺蒋存祺的主要事迹罗伦佐娜维洛娜毛周角化修复液治疗毛周角化有用吗?谁用过?能告诉我吗?丑福晋谁有好看的言情小说介绍下8090lu.com8090看看电影网怎么打不开了www.175qq.com请帮我设计个网名www.dm8.cc有没有最新的日本动漫网站?dpscycle魔兽世界国服,求几个暗影MS的输出宏邯郸纠风网邯郸媒体曝光电话多少国风商讯《国风周南》酒仙琐事"酒仙"指的是什么人?
域名主机空间 已备案未注册域名 如何查询ip地址 搬瓦工官网 42u机柜尺寸 好看的桌面背景图片 typecho 华为4核 52测评网 双拼域名 idc资讯 91vps 中国网通测速 四核服务器 创建邮箱 lamp兄弟连 稳定空间 空间排行榜 sonya zencart安装 更多