Linux shell脚本入门

转自:http://blog.csdn.net/gexiaobaohelloworld/article/details/7973846 点击打开链接

1 Shell环境设置

1.1 登陆欢迎信息

终端机接口 (tty1 ~ tty6) 登陆的时候,会有几行提示的字符串,在  /etc/issue(附加信息可放置在 /etc/motd)中定义。
issue 内的各代码意义:
\d本地端时间的日期
\l显示第几个终端机接口
\m显示硬件的等级 (i386/i486/i586/i686...)
\n显示主机的网络名称
\o显示 domain name
\r操作系统的版本 (相当于 uname -r)
\t显示本地端时间的时间
\s操作系统的名称
\v操作系统的版本

1.2 bash环境配置文件

实线的方向是主要流程,虚线表示被调用的配置文件。在login shell的环境下,最终被读取的配置文件是“~./bashrc”。所以,我们可以将个人需要的设置写入该文件即可。
由于/etc/profile与~/.bash_profile都是在取得login shell的时候才会读取配置文件,所以,如果你将自己的特定设置写入上述文件后,通常需要退出后再登陆配置文件才能生效。其实, 我们也可以使用source命令或小数点(.)将配置文件的内容读入当前shell环境中。
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #source ~/.bashrc  
  2. #.  ~/.bashrc  


1.3 常见的环境变量

1.3.1 环境变量

存储在文件/etc/profile中

$PATH:决定了shell将到哪些目录中寻找命令或程序。
$HOME:当前用户主目录。
$MAIL:是指当前用户的邮件存放目录。
$SHELL:是指当前用户用的是哪种Shell。
$HISTSIZE:是指保存历史命令记录的条数。
$LOGNAME:是指当前用户的登录名。
$HOSTNAME:是指主机的名称,许多应用程序如果要用到主机名的话,通常是从这个环境变量中来取得的。
$LANG/LANGUGE:是和语言相关的环境变量,使用多种语言的用户可以修改此环境变量。
$PS1:是基本提示符,对于root用户是#,对于普通用户是$,也可以使用一些更复杂的值。
$PS2:是附属提示符,默认是“>”。
$IFS:输入域分隔符。当shell读取输入时,用来分隔单词的一组字符,它们通常是空格、制表符和换行符。
$0:shell脚本的名字。
$#:传递给脚本的参数个数。
$$:shell脚本的进程号,脚本程序通常会用它生成一个唯一的临时文件,如/tmp/tmfile_$

1.3.2 设置方法

echo:显示指定环境变量。
export:设置新的环境变量。
env:显示所有环境变量。
set:显示所有本地定义的shell变量。
unset:清除环境变量。

将一个路径加入到PATH变量中:

如在PATH 这个变量当中“累加”一个新目录 这个目录

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #1,控制台中:  
  2. $ PATH="$PATH:/my_new_path"  
  3. #2,修改profile文件:  
  4. $ vi /etc/profile  
  5. 在里面加入:  
  6. export PATH="$PATH:/my_new_path"  
  7. #3,修改.bashrc文件:  
  8. $ vi /root/.bashrc  
  9. 在里面加入:  
  10. export PATH="$PATH:/my_new_path"  
  11. #后两种方法一般需要重新注销系统才能生效,最后可以通过echo命令测试 

1.4 bash中的通配符和特殊符号和组合按键

注意,这里的通配符虽然和正则表达式相似,但是是基于bash解释器解析的,而正则表达式由正则引擎的软件(如awk,grep,sed等)解析,二者完全不同。

1.4.1 通配符

*代表0个或多个任意字符
?代表一个任意字符
[ ][abcd],表示一个字符,或a或b或c或d
[-][0-9],表示一个数字,0到9之间的某个
[^][^abc],表示一个字符,且不是a、b、c
范例:
[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. [root@linux ~]# ls test*     #那个 * 代表后面不论接几个字符都予以接受  
  2. [root@linux ~]# ls test?     #那个 ? 代表后面"一定"要接"一个"字符  
  3. [root@linux ~]# ls test???   #那个 ??? 代表"一定要接三个"字符!  
  4. [root@linux ~]# cp test[1-5] /tmp  # 将 test1, test2, test3, test4, test5 若存在的话,就拷贝到 /tmp  
  5. [root@linux ~]# cp test[!1-5] /tmp # 只要不是 test1, test2, test3, test4, test5 之外的其它 test?拷贝到 /tmp  
  6. [root@linux ~]# cd /lib/modules/`uname -r`/kernel/drivers  # 系统先执行 uname -r 找出输出结果;将结果累加在目录上面,来执行 cd 的功能!  
  7.     = cd /lib/modules/$(uname -r)/kernel  #另外,这个 quot (`) 的功能,也可以利用 $() 来取代喔!  
  8. [root@linux ~]# cp *[A-Z]* /tmp   #表示文件中包含大写字母  
  9. [root@linux ~]# ls -lda /etc/*[35]* #表示文件中包含数字3或者5.  

1.4.2 特殊字符

#注释
\转义字符
|管道(pipe)
;连续命令
~用户主文件夹
$变量前导符
&作业控制的后台运行
!逻辑非
/目录分隔符
>,>>数据流重定向,输出导向,分别是“替换“和”增加“
<,<<数据流重定向,输入导向
' ‘单引号,不具有变量置换功能
” “双引号,具有变量置换功能
` `` `中内容可以先执行的命令,也可以用$( )来替换
( )在中间为子shell的起始与结束
{ }在中间为命令块的组合

1.4.3 组合按键

Ctrl + C终止目前的命令
Ctrl + D输入结束(EOF),例如邮件结束的时候;
Ctrl + M就是 Enter 啦!
Ctrl + S暂停屏幕的输出
Ctrl + Q恢复屏幕的输出
Ctrl + U在提示字符下,将整列命令删除
Ctrl + Z暂停目前的命令

1.5 数据流重定向

linux的文件描述符FD,通常为10个:FD0~FD9。可以理解为linux跟踪打开文件,分配的一个数字,linux启动后会默认打开三个文件描述符:
0标准输入 standard input
1标准输出 standard output
2错误输出 error output 
示例1:
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. ls -al >   newtxt  #本来ls -al 命令预设输出到屏幕,现在被重新导出到newtxt文档。  
  2. ls -al >>  newtxt  #现将ls -al 命令输出结果“追加”到文件newtxt末尾。  
示例2:想要将正确的与错误的数据分别存入不同的档案中:
• 1> :是将正确的数据输出到指定的地方去
• 2> :是将错误的数据输出到指定的地方去
[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. $ find /home -name testing > list_right 2> list_error  
有 Permission 的那几行错误信息都会跑到 list_error 这个档案中,至于正确的输出数据则会存到 list_right 这个档案中。
示例3:
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #将错误信息丢弃(置于垃圾箱中/dev/null)  
  2. $ find /home -name testing > list_right 2> /dev/null   
  3. #将正确输出和错误输出都放置到同一个文件中testing中去  
  4. $ find /home -name testing > list 2> list <==错误写法  
  5. $ find /home -name testing > list 2>&1 <==正确写法  
另外,这里区分一下 command>file  2>file  与  command>file 2>&1
command > file 2>file 的意思是stdout和stderr都直接送到file中, file会被打开两次,这样stdout和stderr会互相覆盖,这样写相当使用了FD1和FD2两个同时去抢占file 的管道。
command >file 2>&1 这条命令就将stdout直接送向file, stderr 继承了FD1管道后,再被送往file,此时,file 只被打开了一次,也只使用了一个管道FD1,它包括了stdout和stderr的内容。

1.6 命令执行的判断依据

分号;cmd;cmd
多条命令顺序执行,执行顺序是从左到右的顺序。
与 &&cmd1 && cmd2
若cmd1执行完毕且正确执行($?=0),则开始执行cmd2
若cmd2执行完毕且为错误($? !=0),则cmd2不执行
或 ||cmd1 || cmd2
若cmd1执行完毕且正确执行($?=0),则cmd2不执行
若cmd2执行完毕且为错误($? !=0),则开始执行cmd2
注意:这里的&&和||没有优先级关系,而是按照命令次序按序执行,所以对于:
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. ls /tmp/abc || mkdir /tmp/abc && touch /tmp/abc/hehe  
该范例总会创建/tmp/abc/hehe文件。而不是先执行后面的。
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. [ -f file1 ] && exit 0 || exit 1  
该范例如果测试成功,则执行第一条命令,否则执行第二条命令。

1.7 shell脚本调试

Shell提供了一些用于调试脚本的选项,如下所示:
-n   读一遍脚本中的命令但不执行,用于检查脚本中的语法错误
-v   一边执行脚本,一边将执行过的脚本命令打印到标准错误输出
-x   提供跟踪执行信息,将执行的每一条命令和结果依次打印出来
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. $ sh -x ./script.sh  

2 语法基本介绍

2.1 开头

程序必须以下面的行开始( 必须放在文件的第一行): 
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh   
符号#!用来告诉系统它后面的参数是用来执行该文件的程序,在这个例子中我们使用/bin/sh来执行程序。 
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. chmod +x filename  
当编辑好脚本时,如果要执行该脚本,还必须使其可执行。 这样才能用./filename 来运行。

2.2 注释 

在进行shell编程时,以#开头的句子表示注释,直到这一行的结束。

文档可以这样写:

[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. ###################################################################################  
  2. ## The script is used for the regression of lineage jar with latest hive-exec..jar  
  3. ## , expression_analyze.jar, parse_common.jar, field_lineage.jar, ast_parser.jar  
  4. ## in trunk.  
  5. ###################################################################################  
区块可以这样写
[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. ###############################################  
  2. ## PARAMETERS  
  3. ##  

2.3 变量 

2.3.1 变量类型默认为字符串类型

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh   
  2. name="Jack"  
  3. s="$name, hello!" #注意等号"="前后都不能有空格。建议字符串用双引号(单引号表示纯字符串,不支持$等)。  
  4. echo $s           #输出Jack, hello!(若s用单引号,则输出$name, hello!)  
  5. unset s           #删除变量s  

2.3.2 单引号,双引号,无引号

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. s="./*"  
  2. echo $s     #这是变量,命令即为echo ./* ,通配符解开为:./1.sh ./a ./b ./c  
  3. echo '$s'   #这里的单引号表示一个字符串,字符串中的$等符号不能转义,输出:$s  
  4. echo "$s"   #这里的双引号表示一个字符串,字符串中的$等符号要转义,其中$s为变量,所以输出./*  

2.3.3 参数扩展——花括号

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. num=2   
  2. echo "this is the $numnd" #this is the   
这并不会打印出"this is the 2nd",而仅仅打印"this is the ",因为shell会去搜索变量numnd的值,但是这个变量时没有值的。可以 使用花括号来告诉shell我们要打印的是num变量: 
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. num=2   
  2. echo "this is the ${num}nd" #this is the 2nd  

这将打印: this is the 2nd 

2.3.4 参数扩展——正则表达式
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. ${name:-default} 使用一个默认值(一般是空值)来代替那些空的或者没有赋值的变量name;   
  2. ${name:=default}使用指定值来代替空的或者没有赋值的变量name;  
  3. ${name:?message}如果变量为空或者未赋值,那么就会显示出错误信息并中止脚本的执行同时返回退出码1。  
  4. ${#name} 给出name的长度  
  5. ${name%word} 从name的尾部开始删除与word匹配的最小部分,然后返回剩余部分  
  6. ${name%%word} 从name的尾部开始删除与word匹配的最长部分,然后返回剩余部分  
  7. ${name#word} 从name的头部开始删除与word匹配的最小部分,然后返回剩余部分  
  8. ${name##word} 从name的头部开始删除与word匹配的最长部分,然后返回剩余部分  
举例如下,使用cjpeg命令将一个GIF的文件转换为JPEG的文件:
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. $ cjpeg image.gif > image.jpg  
  2. 也许有时我们会在大量的文件上进行这样的操作.这时我们如何自动重定向?我们可以很容易的这样来做:  
  3. #!/bin/sh  
  4. for image in *.gif  
  5. do  
  6.    cjpeg $image > ${image%%gif}jpg  
  7. done  

2.3.5 注意变量与变量字符串的问题

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. read a  
  2. if [ $a = "yes" ];then  
  3. echo "OK"  
  4. fi  
  5. if [ "$a" = "yes" ];then  
  6. echo "OK"  
  7. fi  
这里$a是变量a的值,"$a"是对变量a做字符串处理结果。如果read a 时直接回车,那么$a就是空,而不是" ",这时程序变成了 if [ = "yes" ]就错了,所以一般用双引号括起来,表示使用字符串值,且转义其中的$变量

2.3.6 注意打印"*"的问题

[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh  
  2. sql='select * from table' #此处单引号''只在sql变量定义时作用,若变量被调用,则单引号只传递字符串,而                           #不会被延伸至变量调用的语句中
  3. echo  $sql  #其中的*会被替换成所有文件,这就相当于echo select * from table
  4. echo "$sql" #这就相当于echo "select * from table",双引号会防止*通配扩展,而直接识别为*字符
2.3.7 变量/字符串连接
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. names=$var_name1$var_name2   #注意,两个变量之间没有空格  
  2. sentence=${names}"hello"     #直接连接  

2.4 declare

shell中的变量类型默认为字符串类型,但可以通过declare语法来指定变量的其他类型。
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 语法:  
  2. declare [+/-][afrix]  variable  
  3. 说明:  
  4. +/- "-"可用来指定变量的属性,"+"则是取消变量所设的属性。  
  5. -a 定义变量variable为数组(array)类型  
  6. -i  定义变量variable为整数(integer)类型  
  7. -r  定义变量variable为只读(readonly)类型  
  8. -x  用法与export一样,将variable设置为环境变量  

2.5 数值计算

方法1,declare -i

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 示例1:让变量 sum 保存 100+300+50 的加和结果  
  2. [root@linux ~]# sum=100+300+50  
  3. [root@linux ~]# echo $sum       #结果为:100+300+50     
  4. [root@linux ~]# declare -i sum=100+300+50  
  5. [root@linux ~]# echo $sum       #结果为:450          
  6.   
  7. 示例2:生成随机数  
  8. [root@linux ~]# declare -i number=$RANDOM*10/32767 ; echo $number  

方法2,var=$ (( expression ))

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. foo=1  
  2. foo=$(($foo+1))  
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. echo $ (( 13 % 3 ))  

2.6 数组

定义:shell支持一维数组,但并不限定数组大小,数组下标从0开始。
修改:对于数组修改操作,可以再对其重新赋值;
删除:如果要删除一个已经赋值后的元素则需要借助一个外部命令:unset,如:unset array[0]可清空下标为0的元素,此时数组大小减一;unset array[@]可以清空整个数组元素所有元素。
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/bash  
  2.   
  3. #赋值  
  4. a[0]=1              #第一种赋值方法  
  5. a[1]=2  
  6. a[2]=3  
  7. b=(6,7,8,9,10)      #第二种赋值方法  
  8.   
  9. #取值  
  10. echo  ${b[0]}       #用${数组名[下标]},下标是从0开始。下标是*或者@,则得到整个数组内容。输出6  
  11. echo  ${b[*]:1:3}   #直接通过${数组名[@或*]:起始位置:长度}切片原先数组,输出7 8 9  
  12. echo  ${b[*]::3}    #输出6 7 8(前三个元素)  
  13.   
  14. #遍历  
  15. echo  ${b[*]}       #遍历数组除用循环外还可用:“数组名[*]”或“数组名[@]”,输出6 7 8 9 10  
  16. echo  ${b[@]}       #输出:6 7 8 9 10  
  17. for var in ${arr[@]};do echo $var; done    #分行输出:6 7 8 9 10  
  18.   
  19. #数组个数  
  20. echo  ${#b[*]}      #输出:5  
  21. echo  ${#b[@]}      #输出:5  
  22.   
  23. #修改和删除  
  24. b[0]=0              #修改:重新赋值  
  25. echo ${b[*]}        #输出:0 7 8 9 10  
  26. unset  b[0]          #删除:删除第一个元素  
  27. echo ${b[*]}        #输出:7 8 9 10  

2.7 语句块

可以使用{}构造一个语句块。
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. {...}  

2.7 特殊变量

举例说:
脚本名称叫test.sh 入参三个: 1 2 3
运行test.sh 1 2 3后
[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. $* 为"1 2 3"(在一个变量中列出所有参数,各个参数之间用环境变量IFS中的第一个字符分隔开,所以如果IFS置空,则会变成"123")  
  2. $@ 为"1 2 3"($*的变体,不适用环境变量IFS,而是以空格连接参数,所以当IFS置空,则扔为"1 2 3")  
  3. $# 为3(参数数量)  
  4. $0 $n,用来存放函数调用或脚本执行时传入的参数,$0为test.sh  
  5. $1 为1,以此类推  
  6. $$ 脚本运行的当前进程的ID号  
  7. $! 后台运行的最后一个进程的ID号  
  8. $- 显示shell使用的当前选项  
  9. $? 显示最后命令的退出状态,0表示无错误(这个变量也常常用来打印输出,在脚本调试时标记某个shell命令或某个函数是否正确执行,但是要注意,$?记载的是最近的函数或命令的退出状态,因此打印时应该立即打印以获得正确的信息)  

2.8 输入与输出

输入命令read
[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. [root@linux ~]# read [-pt] variable  
参数:
-p :后面可以接提示字符!
-t :后面可以接等待的『秒数!』这个比较有趣~不会一直等待使用者啦!

范例:提示使用者 30 秒内输入自己的大名,将该输入字符串做成 name 变量
[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. [root@linux ~]# read -p "Please keyin your name: " -t 30 name  
  2. Please keyin your name: VBird Tsai  
  3. [root@linux ~]# echo $name  
  4. VBird Tsai  
输出命令echo(默认有换行符)
echo函数定义了一组转义字符,在使用转义字符时要加入”-e”选项。其转义字符如下:
“\a” :响铃报警,”\b” :后退一字符,”\f” :换页,”\n” :显示换行,”\t” :制表符,”\v” :垂直制表符,”\r” :回车符,”\\” :反斜线。
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. echo -e hello,"\n"world!   

设置颜色

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 设置彩色文本 重置=0 黑色=30 红色=31 绿色=32...  
  2. echo -e "\e[1;31m This is red text \e[0m"  
  3. 设置彩色背景 重置=0 黑色=40 红色=41 绿色=42...  
  4. echo -e "\e[1;42m This is green background \e[0m"  

注意:如果alias ls = 'ls --color' 这时候 ls | xargs tar -czvf 1.tar.gz会出现以上类似的错误tar: \033[00mxy_psp_proto.h\033[00m: Cannot stat: No such file or directory

用echo -n 去掉末尾的换行符。

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. echo -n "string to output"  

输出命令printf(默认无换行符)

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. printf "hello"  

2.9 Here Document:说明文字

对每个脚本写一段帮助性的文字是很有用的,此时如果我们有那个here documents,就不必用echo函数一行行输出 。一个 "Here document" ,是以特殊符号 << 开头,后面接一个界定字符串 limit string,这个字符串还必须出现在 here document 命令序列的末尾。下面是一个例子,在该例子中,我们对多个文件进行重命名,并且使用here documents打印帮助:
[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh  
  2. # we have less than 3 arguments. Print the help text:  
  3. if [ $# -lt 3 ] ; then  
  4.     cat << EOF  
  5. [批量换名:对文件名进行批量替换]  
  6. USAGE:      ren 'regexp' 'replacement' files...  
  7. EXAMPLE:    rename all *.HTM files in *.html:  
  8.     sh $0 'HTM$' 'html' *.HTM  
  9. HELP  
  10. EOF  
  11.     exit 0  
  12. fi  
  13.   
  14. OLD="$1"  
  15. NEW="$2"  
  16. # The shift command removes one argument from the list of  
  17. # command line arguments.  
  18. shift  
  19. shift  
  20. # $* contains now all the files:  
  21. for file in $*; do  
  22.     if [ -f "$file" ] ; then  
  23.         #这里的|是管道,将源文件名进行词汇替换  
  24.         newfile=`echo "$file" | sed "s/${OLD}/${NEW}/g"`  
  25.         if [ -f "$newfile" ]; then  
  26.             echo "ERROR: $newfile exists already"  
  27.         else  
  28.             echo "renaming $file to $newfile ."  
  29.             mv "$file" "$newfile"  
  30.         fi  
  31.     fi  
  32. done  
分析:

这是一个复杂一些的例子。让我们详细讨论一下。第一个if表达式判断输入命令行参数是否小于3个 (特殊变量$# 表示包含参数的个数) 。如果输入参数小于3个,则将帮助文字传递给cat命令,然后由cat命令将其打印在屏幕上。打印帮助文字后程序退出。 如果输入参数等于或大于3个,我们就将第一个参数赋值给变量OLD,第二个参数赋值给变量NEW。下一步,我们使用shift命令将第一个和第二个参数从 参数列表中删除,这样原来的第三个参数就成为参数列表$*的第一个参数。然后我们开始循环,命令行参数列表被一个接一个地被赋值给变量$file。接着我 们判断该文件是否存在,如果存在则通过sed命令搜索和替换来产生新的文件名。然后将反短斜线内命令结果赋值给newfile。这样我们就达到了我们的目 的:得到了旧文件名和新文件名。然后使用mv命令进行重命名。  

2.10 退出状态

在Linux系统中,每当命令执行完成后,系统都会返回一个退出状态。该退出状态用一整数值表示,用于判断命令运行正确与否。
若退出状态值为0,表示命令运行成功
若退出状态值不为0时,则表示命令运行失败
最后一次执行的命令的退出状态值被保存在内置变量“$?”中,所以可以通过echo语句进行测试命令是否运行成功

2.11 测试结构与操作符

测试结构
测试命令可用于测试表达式的条件的真假。如果测试的条件为真,则返回一个 0值;如果测试的条件为假,将返回一个非 0整数值。
测试命令有两种结构:
一种命令是使用test命令进行测试,格式为: 
[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. test expression  
其中条件expression是一个表达式,该表达式可由数字、字符串、文本和文件属性的比较,同时可加入各种算术、字符串、文本等运算符。
另一种命令格式:
[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. [ expression ]  
其中“[ ”是启动测试的命令,但要求在expression后要有一个“ ]”与其配对。使用该命令时要特别注意“[”后和“]”前的空格是必不可少的。
可以使用 man test来查询所有操作符。

整数比较运算符
num1 -eq num2 如果 num1等于num2,测试结果为0
num1 -ge num2 如果 num1大于或等于num2,测试结果为0
num1 -gt num2 如果 num1大于num2,测试结果为0
num1 -le num2 如果 num1小于或等于num2,测试结果为0
num1 -lt num2 如果 num1小于num2,测试结果为0
num1 -ne num2 如果 num1不等于num2,测试结果为0

字符串运算符
字符串运算符用于测试字符串是否为空、两个字符串是否相等或者是否不相等
string          测试字符串string是否不为空
-n string  测试字符串string是否不为空
-z string  测试字符串string是否为空
string1 = string2  测试字符串string1是否与字符串string2相等
string1 != string2  测试字符串string1是否与字符串string2 不相等

文件操作符
Linux Shell提供了大量的文件操作符,这样可以完成测试文件的各种操作。比较常用的文件操作符如下表所示:
-d file          测试file是否为目录
-e file          测试file是否存在
-f file          测试file是否为普通文
-r file          测试file是否是可读文件
-s file          测试file的长度是否不为0
-w file          测试file是否是可写文件
-x file         测试file是否是可执行文件
-L file          测试file是否符号化链接

逻辑运算符:
逻辑运算符主要包括逻辑非、逻辑与、逻辑或运算符:
!expression     如果expression为假,则测试结果为真
expression1 –a expression2  如果expression1和expression同时为真,则测试结果为真
expression1 –o expression2     如果expression1和expression2中有一个为真,则测试条件为真
----------------------------------------------------------------------------------------------------------------------------------------
算术运算符
在Linux Shell中,算术运算符包括:+(加运算)、-(减运算)、*(乘运算)、/(除运算)、%(取余运算)、**(幂运算),这些算术运算符的举例及其结果如下表所示:
运算符            举例      结果
+(加运算)    3+5       8
-(减运算)    5-3       2
*(乘运算)    5*3       15
/(除运算)    8/3        2
%(取余运算)    15%4        3
**(幂运算)    5**3       125

位运算符
位运算符在Shell编程中很少使用,通常用于整数间的运算,位运算符是针对整数在内存中存储的二进制数据流中的位进行的操作。
<<(左移)         value=4<<2  4左移2位,value值为16
>>(右移)         value=8>>2   8右移2位,value值为2
&(按位与)         value=8&4   8按位与4,value值为0
|(按位或)         value=8|4   8按位或4,value值为12
~(按位非)          value=~8     按位非8,value值为-9
^(按位异或)          value=10^3    10按位异或3,value值为9

自增自减运算符
自增自减操作符主要包括前置自增(++variable) 、前置自减(--variable)、后置自增(variable++)和后置自减(variable--)。
前置操作首先改变变量的值(++用于给变量加1,--用于给变量减1),然后在将改变的变量值交给表达式使用
后置变量则是在表达式使用后再改变变量的值
要特别注意自增自减操作符的操作元只能是变量,不能是常数或表达式,且该变量值必须为整数型,例如:++1、(num+2)++都是不合法的

3 shell命令与流程控制

在shell脚本中可以使用三类命令: 

3.1 Unix 命令: 

shell脚本中如何使用unix命令:
1,直接使用(一行一个)
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. ls | grep "core"  
2,使用反斜线
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. tar -zcvf lastmod.tar.gz `find . -mtime -1 -type f -print`   
3,使用$()
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. more $(grep -l POSIX *)  

常用命令语法及功能

echo "some text"将文字内容打印在屏幕上 
ls文件列表 
wc –l file
wc -w file
wc -c file
计算文件行数
计算文件中的单词数
计算文件中的字符数 
cp sourcefile destfile文件拷贝 
mv oldname newname重命名文件或移动文件 
rm file删除文件 
grep 'pattern'  file在文件内搜索字符串比如:grep 'abc' 1.txt
cut -b colnum file指定欲显示的文件内容范围,并将它们输出到标准输出设备
比如:输出每行第5个到第9个字符cut -b5-9 file.txt
千万不要和cat命令混淆,这是两个完全不同的命令 
cat file.txt输出文件内容到标准输出设备(屏幕)上 
file somefile得到文件类型 
read var 提示用户输入,并将输入赋值给变量 
sort file.txt 对file.txt文件中的行进行排序 
uniq删除文本文件中出现的行列
比如: sort file.txt | uniq 
expr 进行数学运算Example: add 2 and 3expr 2 "+" 3 
find 搜索文件
比如:根据文件名搜索find . -name filename -print 
tee 将数据输出到标准输出设备(屏幕) 和文件
比如:somecommand | tee outfile 
basename file返回不包含路径的文件名
比如: basename /bin/tux将返回 tux
dirname file返回文件所在路径
比如:dirname /bin/tux将返回 /bin
head file
tail file
打印文本文件开头几行
打印文本文件末尾几行
sed

Sed是一个基本的查找替换程序。
可以从标准输入(比如命令管道)读入文本,
并将结果输出到标准输出(屏幕)。
该命令采用正则表达式(见参考)进行搜索。 
不要和shell中的通配符相混淆。
比如:将linuxfocus 替换为 LinuxFocus :
cat text.file | sed 's/linuxfocus/LinuxFocus/' > newtext.file

awk

awk 用来从文本文件中提取字段。
缺省地,字段分割符是空格,可以使用-F指定其他分割符。
cat file.txt | awk -F, '{print $1 "," $3 }'
这里我们使用,作为字段分割符,同时打印第一个和第三个字段。
如果该文件内容如下: Adam Bor, 34, IndiaKerry Miller, 22, USA
命令输出结果为:Adam Bor, IndiaKerry Miller, USA 

3.3 流程控制 

3.3.1.if语句 

"if" 表达式 如果条件为真则执行then后面的部分: 
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. if ....; then   
  2.   ....   
  3. elif ....; then   
  4.   ....   
  5. else   
  6.   ....   
  7. fi  
大多数情况下,可以使用测试命令来对条件进行测试。比如可以比较字符串、判断文件是否存在及是否可读等等… 

通常用" [ ] "来表示条件测试注意这里的空格很重要。要确保方括号的空格。 

[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. [ ! -d $CASE ] && echo "No case path $CASE, quit!" && exit 1 #判断如果没有目录$CASE就退出  

[ -f "somefile" ]  :判断是否是一个文件 
[ -x "/bin/ls" ]  :判断/bin/ls是否存在并有可执行权限 
[ -n "$var" ]  :判断$var变量是否有值 
[ "$a" = "$b" ]  :判断$a和$b是否相等 
执行man test可以查看所有测试表达式可以比较和判断的类型。 

直接执行以下脚本: 

[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh  
  2. if [ "$SHELL" = "/bin/bash" ]; then  
  3.     echo "your login shell is the bash (bourne again shell)"  
  4. else  
  5.     echo "your login shell is not bash but $SHELL"  
  6. fi  

变量$SHELL包含了登录shell的名称,我们和/bin/bash进行了比较。 

快捷操作符 
熟悉C语言的朋友可能会很喜欢下面的表达式: 
[ -f "/etc/shadow" ] && echo "This computer uses shadow passwors" 
这里 && 就是一个快捷操作符,如果左边的表达式为真则执行右边的语句。您也可以认为是逻辑运算中的与操作。上例中表示如果/etc/shadow文件存在则打印” This computer uses shadow passwors”。同样或操作(||)在shell编程中也是

可用的。这里有个例子:

[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh  
  2. mailfolder=./lineage_case  
  3. [ ! -r "$mailfolder" ]&&{ echo "Can not read $mailfolder" ; exit 1; }  
  4. dir="${mailfolder}/*"  
  5. #grep "^From" `echo "$dir"` #  
  6. grep "^From" $dir  #这两者效果相同,关键在于要知道这里的*要表达的是真正的*,还是表示通配,这里表示通配  
  7.   
  8. sql="select * from table;"  
  9. echo "$sql"        #这里要表达的是真正的*,而不是通配  

该脚本首先判断mailfolder是否可读。如果可读则打印该文件中的"From" 一行。如果不可读则或操作生效,打印错误信息后脚本退出。{}构成匿名函数。

3.3.2 case

case :表达式可以用来 匹配一个给定的字符串,而不是数字。 
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. case ... in   
  2. ...) do something here ;;   
  3. esac   
让我们看一个例子。 file命令可以辨别出一个给定文件的文件类型,比如: 
file lf.gz 
  这将返回: 
lf.gz: gzip compressed data, deflated, original filename, 
last modified: Mon Aug 27 23:09:18 2001, os: Unix 
我们利用这一点写了一个叫做smartzip的脚本,该脚本可以自动解压bzip2, gzip 和zip 类型的压缩文件: 
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh   
  2. ftype=`file "$1"`   
  3. case "$ftype" in   
  4. "$1: Zip archive"*)   
  5.   unzip "$1" ;;   
  6. "$1: gzip compressed"*)   
  7.   gunzip "$1" ;;   
  8. "$1: bzip2 compressed"*)   
  9.   bunzip2 "$1" ;;   
  10. *) echo "File $1 can not be uncompressed with smartzip";;   
  11. esac   
您可能注意到我们在这里使用了一个特殊的变量$1。该变量包含了传递给该程序的第一个参数值。也就是说,当我们运行: 
smartzip articles.zip 
$1 就是字符串 articles.zip 

再看一例

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh  
  2. echo “Is it morning? Please answer yes or no”  
  3. read timeofday  
  4. case “$timeofday” in  
  5.     [yY] | [Yy][Ee][Ss])  
  6.            echo “Good Morning”;;  
  7.     [nN]*)  
  8.            echo “Good Afternoon”;;  
  9.     *)  
  10.            echo “Sorry, answer not recognized”  
  11.            exit 1  
  12.            ;;  
  13. esac  
  14. exit 0  

3.3.3. selsect

select 表达式是一种bash的扩展应用,尤其擅长于交互式使用。用户可以从一组不同的值中进行选择。 
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. select var in ... ; do   
  2.  break   
  3. done   
  4. .... now $var can be used ....   
下面是一个例子: 
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh   
  2. echo "What is your favourite OS?"   
  3. select var in "Linux" "Gnu Hurd" "Free BSD" "Other"; do   
  4.     break   
  5. done   
  6. echo "You have selected $var"   
下面是该脚本运行的结果: 
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. What is your favourite OS?   
  2. 1) Linux   
  3. 2) Gnu Hurd   
  4. 3) Free BSD   
  5. 4) Other   
  6. #? 1   
  7. You have selected Linux   

3.3.4.while

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. while表达式:   
  2. while ...; do   
  3. ....   
  4. done   

while-loop 将运行直到表达式测试为真。关键字"break" 用来跳出循环。而关键字”continue”用来不执行余下的部分而直接跳到下一个循环。 

例如:

[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh  
  2. i=1  
  3. while [ "$i" -le 10 ];do  
  4.     echo "num is $i"  
  5.     i=$((i+1))  
  6. done  
或:
[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh  
  2. i=1  
  3. while [ "$i" -le 10 ]  
  4. do  
  5.     echo "num is $i"  
  6.     i=$((i+1))  
  7. done  

3.3.5 until

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. until 条件  
  2. do  
  3.   语句  
  4. done  

以下代码直到有内容时返回值才为0,因此跳过do|done,然后输出echo。其中的>/dev/null只是为了将结果不在屏幕显示,不影响返回值。

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. until who | grep "$1" > /dev/null  
  2. do  
  3.  sleep(60)  
  4. done  
  5.   
  6. echo "$1 has logged in"  

3.3.6.for

for-loop表达式查看一个字符串列表 (字符串用空格分隔) 然后将其赋给一个变量: 
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. for var in ....; do   
  2.   ....   
  3. done   
在下面的例子中,将分别打印ABC到屏幕上: 
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh   
  2. for var in A B C ; do   
  3.   echo "var is $var"   
  4. done   
下面是一个更为有用的脚本showrpm,其功能是打印一些RPM包的统计信息: 
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh   
  2. # list a content summary of a number of RPM packages   
  3. # USAGE: showrpm rpmfile1 rpmfile2 ...   
  4. # EXAMPLE: showrpm /cdrom/RedHat/RPMS/*.rpm   
  5. for rpmpackage in $*; do   
  6.  if [ -r "$rpmpackage" ];then   
  7.   echo "=============== $rpmpackage =============="   
  8.   rpm -qi -p $rpmpackage   
  9.  else   
  10.   echo "ERROR: cannot read file $rpmpackage"   
  11.  fi   
  12. done   
这里出现了第二个特殊的变量$*,该变量包含了所有输入的命令行参数值。
如果您运行showrpm openssh.rpm w3m.rpm webgrep.rpm ,此时 $* 包含了 3 个字符串,即openssh.rpm, w3m.rpm and webgrep.rpm. 

for的另一种用法:

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. for (( 初始值; 限制值; 执行步长 ))  
  2. do  
  3.      程序段  
  4. done  

例如:

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/bash  
  2. # Program:  
  3. #   Try do calculate 1+2+....+${your_input}  
  4. # History:  
  5. # 2005/08/29    VBird   First release  
  6. PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin  
  7. export PATH  
  8.   
  9.   
  10. read -p "Please input a number, I will count for 1+2+...+your_input: " nu  
  11.   
  12.   
  13. s=0  
  14. for (( i=1; i<=$nu; i=i+1 ))  
  15. do  
  16.     s=$(($s+$i))  
  17. done  
  18. echo "The result of '1+2+3+...+$nu' is ==> $s"  

3.3.7 beak

退出循环,类似C。
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. break  

3.3.8 continue

进入下一次循环,类似C。
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. continue  

3.4 source命令和dot命令

通常,当一个脚本执行一条外部命令或者脚本程序时,它会创建一个新的环境,也就是一个子shell,命令将在这个新环境中执行,在执行完毕后,这个环境被丢弃,留下退出码返回给父shell。但是外部的source命令和点命令在执行脚本程序中列出的命令时,使用的是调用该脚本程序的同一个shellsource命令或dot命令将外部命令放在当前进程(而不是子进程)环境中运行(类似c语言中的 #include语句),并继承当前进程(shell)的所有变量

范例:用dot命令修改当前进程的环境变量
以下是文件classic_set (当前目录下)的内容:

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. version=classic  
  2. PATH=/usr/local/old_bin:/usr/bin:/bin:.   
  3. PS1=“classic>”  
以下是文件latest_set (当前目录下)的内容:
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. version= latest  
  2. PATH=/usr/local/new_bin:/usr/bin:/bin:.  
  3. PS1=“latest >”  
用dot 命令执行这两个shell脚本:
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. $ . ./classic_set   
  2. classic> echo $version   
  3. classic   
  4. classic> . latest_set   
  5. latest > echo $version   
  6. latest   
  7. latest >  

3.5 export命令

在默认情况下,在一个shell里创建的变量在此shell调用的下级(子)shell里是不可用的。

export命令将作为它参数的变量导出到子shell中,并使之在子shell中有效,一旦一个变量被shell到处,它就可以被该shell调用的任何脚本使用,也可以被后续依次调用的任何shell使用。

3.6 exec命令

将当前的shell替换为一个不同的程序,wall会替换整个脚本,然后原脚本exec语句后面的脚本内容都不会再继续运行。

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. ...  
  2. exec wall "that's all,we will finish now"  
  3. ...  

3.7 shift命令

依次扫描所有的位置参数,每运行一次(shift不带参数)销毁第一个参数,后面的参数自动前移。
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh  
  2. while [ "$1" != "" ];do  
  3.     echo "$1"  
  4.     shift  
  5. done  
  6. exit 0  

执行 ./shift.sh 12 34 56
输出结果为: 
12
34
56
若将脚本中的 shift 语句删除,则
执行 ./shift.sh 12 34 56
输出结果为:
12
12
12
...(无限循环打印 $1)

3.8 trap命令

trap命令用来指定在接收到操作系统信号之后将要采取的行动。脚本程序通常是从头到尾解释并运行的,所以必须把trap命令放在准备保护的脚本程序部分之前

trap命令常见的用途在于脚本程序中断时完成清理工作,比如临时文件等。例子程序如下:

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh  
  2. trap 'rm -f /tmp/my_tmp_file_$$' INT  
  3. echo creating file /tmp/my_tmp_file_$$  
  4. date > /tmp/my_tmp_file_$$  
  5. echo "press interrupt (CTRL-C) to interrupt ......"  
  6. while [ -f /tmp/my_tmp_file_$$ ]; do  
  7.        echo File exists  
  8.        sleep 3  
  9. done  
  10. echo we never get here  
  11. exit 0  

在这个脚本程序里,先用trap命令安排它在出现INT(中断)信号时执行“rm -f /tmp/my_tmp_file_$$”命令删除临时文件。然后让脚本程序进入一个while循环,在临时文件存在的情况下不断循环。当用户按下CTRL-C组合键时,语句rm -f /tmp/my_tmp_file_$$ 就会被执行,然后继续下一个循环。但是因为临时文件已经被删除了,所以while循环在第一次执行时就会正常退出。注:$$表示当前的shell进程号。运行该脚本,首先在tmp下创建临时文件my_tmp_file_$$,如果遇到INT中断,即Ctrl+c,即删除该文件。


5 函数

5.1 函数定义语法 

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. [ function ] funname [()]  
  2. {  
  3.     action;  
  4.     [return int;]  
  5. }   

说明:
1、可以带function f()  定义,也可以直接f() 定义,不带任何参数。
2、函数的参数默认保存在$1,$2,...中,$0中保存函数名。
3、函数的返回值默认是最后一条命令的运行结果,除非显示有return n(0-255)。返回值由$?获取。
4、先定义后使用。

5.2 函数返回值的两种办法

其一,函数内部使用 return 这个命令,出函数后立刻用$?访问,return只能返回非负数整数 且超过255的会反卷,也就是256就会变成0。

函数内部:return,函数外部:echo $?

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!bin/sh  
  2. function sum()    
  3. {  
  4.     return $(($1+$2))  
  5. }  
  6.   
  7. sum 1 2  
  8. echo $?  

其二,函数内部使用echo这个命令输出想要返回的值,同时调用函数的时候把函数的执行结果赋值给一个变量,那么这个变量里才是真正想要的返回值。

函数内部 echo,函数外部:c = $( sum 1 2)

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!bin/sh  
  2. function sum()    
  3. {  
  4.     echo $(($1+$2))  
  5. }  
  6.   
  7. c=$(sum 1 2)  
  8. echo $c  

5.3 实例分析

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!bin/sh  
  2. function sum()    
  3. {  
  4.     echo $(($1+$2))  
  5.     return $(($1-$2))  
  6. }  
  7. sum $1 $2       #这里因为调用函数,所以输出5+2=7  
  8. c=$(sum $1 $2)  #这里调用函数被当做一条命令,因此其中的echo不起作用,因此只将函数体内最后一条语句的结果(而非返回值)保存在c中,即echo的结果。5+2=7.   
  9. echo $?         #这是上一个函数调用的返回值,为3,(这里用$?,所以函数内部找return)  
  10. echo $c         #这里用$c,所以函数内部找echo  
运行结果:
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. $ . 1.sh 5 2  
  2. 7  
  3. 3  
  4. 7  
c=$(sum $1 $2);  在shell中,但括号里是命令语句。 因此,我们可以将shell中函数,看作是定义一个新的命令。

范例:下面是一个叫做xtitlebar的脚本,使用这个脚本您可以改变终端窗口的名称。这里使用了一个叫做help的函数。正如您可以看到的那样,这个定义的函数被使用了两次。 

[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh  
  2. # vim: set sw=4 ts=4 et:  
  3. help()  
  4. {  
  5.     cat <<EOF  
  6. xtitlebar -- change the name of an xterm, gnome-terminal or kde konsole  
  7. USAGE: xtitlebar [-h] "string_for_titelbar"  
  8. OPTIONS: -h help text  
  9. EXAMPLE: xtitlebar "cvs"  
  10. HELP  
  11. EOF  
  12.     exit 0  
  13. }  
  14. # in case of error or if -h is given we call the function help:  
  15. [ -z "$1" ] && help  
  16. "$1" = "-h" ] && help  
  17. # send the escape sequence to change the xterm titelbar:  
  18. echo -e "33]0;$107"  
再例:

我们已经见过$* 和 $1, $2 ... $9 等特殊变量,这些特殊变量包含了用户从命令行输入的参数。迄今为止,我们仅仅了解了一些简单的命令行语法(比如一些强制性的参数和查看帮助的-h选项)。 但是在编写更复杂的程序时,您可能会发现您需要更多的自定义的选项。通常的惯例是在所有可选的参数之前加一个减号,后面再加上参数值 (比如文件名)。
[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh  
  2. help()  
  3. {  
  4.     cat <<EOF  
  5. This is a generic command line parser demo.  
  6. USAGE EXAMPLE: cmdparser -l hello -f -- -somefile1 somefile2  
  7. HELP  
  8. EOF  
  9.     exit 0  
  10. }  
  11. while [ -n "$1" ]; do  
  12. case $1 in  
  13.     -h) help;shift 1;; # function help is called  
  14.     -f) opt_f=1;shift 1;; # variable opt_f is set  
  15.     -l) opt_l=$2;shift 2;; # -l takes an argument -> shift by 2  
  16.     --) shift;break;; # end of options  
  17.     -*) echo "error: no such option $1. -h for help";exit 1;;  
  18.     *) break;;  
  19. esac  
  20. done  
命令: 
cmdparser -l hello -f -- -somefile1 somefile2 
结果: 
opt_f is 1
opt_l is hello
first arg is -somefile1
2nd arg is somefile2

原理:
这个脚本是如何工作的呢?脚本首先在所有输入命令行参数中进行循环,将输入参数与case表达式进行比较,如果匹配则设置一个变量并且移除该参数。根据unix系统的惯例,首先输入的应该是包含减号的参数.

6 我的脚本模板

templet.sh
[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. ###################################################################################  
  2. ## The script is used for the test of ABC.jar in myApps  
  3. ## @author:xing.gexing  
  4. ## @date 2012-10-26 下午4:05:07  
  5. ## @version V1.0  
  6. ###################################################################################  
  7.   
  8.   
  9. ###############################################  
  10. ## PARAMETERS  
  11. ##  
  12. SMOKE='true'  
  13. SMOKE_MAILTO='xing.gexing\@xxx.com'  
  14. MAILTO='xing.gexing\@xxx.com'  
  15. WHITE_LIST='white.list'  
  16.   
  17. DIR='/home/admin'  
  18. WORKSPACE="${DIR}/myApps"  
  19. [ ! -d $WORKSPACE ] && echo "No case path $WORKSPACE, quit!" && exit 1  
  20.   
  21. ##############################################  
  22. ## BUILD ABC.jar  
  23. ##  
  24. SOURCE="$WORKSPACE/source"  
  25. DEST="$WORKSPACE/dest"  
  26. cd $TRUNK  
  27. svn up --username=xxx --password=ooo  
  28. [ $? -ne 0 ] && echo "svn up failed!!! quit!" && exit 1  
  29. ant clean && ant package  
  30. [ $? -ne 0 ] && echo "build ABC.jar failed!!! quit!" && exit 1  
  31. cp $TRUNK/build/dist/*.jar $DEST/lib  
  32.   
  33.   
  34. "$SMOKE" == 'true' ] && MAILTO=$SMOKE_MAILTO && WHITE_LIST=$WORKSPACE/white.list  
  35.   
  36. sed -i "s@#MAIL_TO#@$MAILTO@" $ENV_CFG  
  37. sed -i "s@#WHITE_LIST#@$WHITE_LIST@" $ENV_CFG  
  38. sed -i "s@#TRUNK_PATH#@$WORKSPACE@" $ENV_CFG  
  39.   
  40. ###############################################  
  41. ## run  
  42. ##  
  43. python test.py -abcd  

7 实例

[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/sh  
  2.   
  3. ##############################################    
  4. #Author GE Xing  
  5. #Time   2013 05 16  
  6.   
  7. ##############################################  
  8. # column title  
  9. #  
  10. echo -e "Reduce, jvm start, reduce done, reduce duration, write size, writing start, writing end, writing duration, real writing start, real writing end, real writing duration"  
  11.   
  12.   
  13. for line in `cat "./ids.result"`  
  14. do  
  15.   
  16.     ##############################################    
  17.     ## task_name(REDUCE)    
  18.     ##   
  19.     reduce_name=$line  
  20.     #echo ----reduce_name:   
  21.     echo -e "${reduce_name}, \c"  
  22.   
  23.   
  24.     ##############################################  
  25.     ## jvm start time  
  26.     ##  
  27.     jvm_start=`cat mapAndReduce.result| grep $reduce_name | grep "given task" | awk '{print $1, $2}'`  
  28.     #time_jvm_start=`date -d "$jvm_start" +%s`  
  29.     time_jvm_start=$(transTime "$jvm_start")  
  30.     #echo ----jvm_start:   
  31.     echo -e "${time_jvm_start}, \c"  
  32.   
  33.   
  34.     ##############################################  
  35.     ## reduce done   
  36.     ##  
  37.     reduce_done=`cat mapAndReduce.result| grep $reduce_name | grep " is in commit-pending, task state:" | awk '{print $1, $2}'`  
  38.     time_reduce_done=$(transTime "$reduce_done")  
  39.     #echo ----reduce_done:   
  40.     echo -e "${time_reduce_done}, \c"  
  41.   
  42.   
  43.   
  44.     ##############################################  
  45.     ## reduce duration  
  46.     ##  
  47.     reduce_duration=`expr $time_reduce_done - $time_jvm_start`  
  48.     #echo ----reduce_duration:   
  49.     echo -e "${reduce_duration}, \c"  
  50.   
  51.   
  52.     ##############################################  
  53.     ## write size  
  54.     ##  
  55.     write_size=`cat mapAndReduce.result| grep $reduce_name | grep "op: HDFS_WRITE, cliID:" | awk 'NR==1 {print $10}'`  
  56.     #echo ----write_size:   
  57.     echo -e "${write_size} \c"  
  58.   
  59.   
  60.     ##############################################  
  61.     ## writing start time  
  62.     ##  
  63.     writing_start=`cat mapAndReduce.result| grep $reduce_name | grep "NameSystem.allocateBlock" | awk '{print $1, $2}'`  
  64.     time_writing_start=$(transTime "$writing_start")  
  65.     #echo ----writing_start:   
  66.     echo -e "${time_writing_start}, \c"  
  67.   
  68.   
  69.     ##############################################  
  70.     ## writing end time  
  71.     ##  
  72.     writing_end=`cat mapAndReduce.result| grep $reduce_name | grep "Removing lease on  file" | awk '{print $1, $2}'`  
  73.     time_writing_end=$(transTime "$writing_end")   
  74.     #echo ----writing_end:   
  75.     echo -e "${time_writing_end}, \c"  
  76.   
  77.   
  78.   
  79.     ##############################################  
  80.     ## writing duration  
  81.     ##  
  82.     writing_duration=`expr $time_writing_end - $time_writing_start`  
  83.     #echo ----writing_duration:   
  84.     echo -e "${writing_duration}, \c"  
  85.   
  86.     ##############################################  
  87.     ## real writing start time  
  88.     ##  
  89.     T=`cat mapAndReduce.result| grep $reduce_name | grep "op: HDFS_WRITE, cliID:" | awk '{print $1, $2, $22}'`  
  90.     #echo -e "T $T\n"  
  91.     t1=`echo $T |awk '{print $1, $2}'`  
  92.     #echo -e "t1 $t1\n"  
  93.     r1=$(transTime "$t1")  
  94.     #echo -e "r1 $r1\n"  
  95.     d1=`echo $T |awk '{print $3/1000}'`  
  96.     #echo -e "d1 $d1\n"  
  97.     b1=`expr $r1 - $d1`  
  98.     #echo -e "b1 $b1\n"  
  99.   
  100.     #t2=`echo $T |awk '{print $4, $5}'`  
  101.     #r2=`date -d "$t2" +%s`  
  102.     #d2=`echo $T |awk '{print $6}'`  
  103.     #b2=`expr $r2 - $d2`  
  104.     #  
  105.     #t3=`echo $T |awk '{print $7, $8}'`  
  106.     #r3=`date -d "$t3" +%s`  
  107.     #d3=`echo $T |awk '{print $9}'`  
  108.     #b3=`expr $r3 - $d3`  
  109.     #  
  110.     #min=$b1  
  111.     #if test $b2 -lt $min  
  112.     #   then   
  113.     #       min=$b2  
  114.     #fi   
  115.     #if test $b3 -lt $min  
  116.     #   then   
  117.     #       min=$b3  
  118.     #fi   
  119.     #  
  120.     #max=$b1  
  121.     #if test $b2 -gt $max  
  122.     #   then   
  123.     #       max=$b2  
  124.     #fi   
  125.     #if test $b3 -gt $max  
  126.     #   then   
  127.     #       max=$b3  
  128.     #fi  
  129.        
  130.     real_writing_start=$b1  
  131.     #echo ----real_writing_start:  
  132.     echo -e "${real_writing_start}, \c"  
  133.   
  134.     ##############################################  
  135.     ## real_writing end time  
  136.     ##  
  137.     real_writing_end=$r1  
  138.     #echo ----real_writing_end:  
  139.     echo -e "${real_writing_end}, \c"  
  140.   
  141.     ##############################################  
  142.     ## real_writing duration  
  143.     ##  
  144.     #real_time_writing_start=`date -d "$real_writing_start" +%s`  
  145.     #real_time_writing_end=`date -d "$real_writing_end" +%s`  
  146.     #real_writing_duration=`expr $real_time_writing_end - $real_time_writing_start`  
  147.     real_writing_duration=`expr $real_writing_end - $real_writing_start`  
  148.     #echo ----real_writing_duration:  
  149.     echo -e "${real_writing_duration}"  
  150.   
  151.   
  152. done  
  153.   
  154. function transTime  
  155. {  
  156.     echo "$1" | awk -F'[:, ]' '{print ($4+$3*60+$2*60*60)*1000+$5}'  
  157. }  

 bash -v sum.sh表示将代码执行之前,先将代码显示出来

[gexing111@gexing111 myapps]$ bash -v sum.sh

#!/bin/bash
let a=30
let b=20
let sum="$a + $b"
if test $a -lt $b
 then
   echo "OK"
 else
   echo "NO"
fi

NO

 bash -v sum.sh表示将代码执行过程中显示出来

[gexing111@gexing111 myapps]$  bash -x sum.sh
+ let a=30
+ let b=20
+ let 'sum=30 + 20'
+ test 30 -lt 20
+ echo NO

NO

程序1:Hello.sh

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/bash  
  2. clear  
  3. echo "Welcome to my first Linux Program!"  
  4. echo "Enter your name :"  
  5. read response  
  6. echo "Hello,$response!"  

程序2:readEmployees.sh

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/bash  
  2. clear  
  3. echo "Plz input your Name"  
  4. read Name  
  5. echo "Plz input your Age"  
  6. read Age  
  7. echo "Name:$Name Age:$Age">>mydata.dat  
  8.   
  9. echo ""  
  10. echo "Employees:"  
  11. echo ""  
  12. cat mydata.dat  

程序3:sum.sh

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/bash  
  2. let a=30  
  3. let b=20  
  4. let sum="$a + $b"  
  5. if test $a -lt $b  
  6.  then  
  7.    echo "OK"  
  8.  else  
  9.    echo "NO"  
  10. fi  

程序4:mymenu.sh

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/bash  
  2. declare flag="1"  
  3. while [ $flag -eq "1" ]  
  4. do  
  5.         echo "The Telephone Book"  
  6.         echo ""  
  7.         echo "1.Display A Telephone Number"  
  8.         echo "2.Add A New Telephone NUmber"  
  9.         echo ""  
  10.         echo "Q Quit"  
  11.         echo ""  
  12.         echo "Enter your selection:"  
  13.         read selection  
  14.   
  15.         case $selection in  
  16.         "1")  
  17.                 echo "You want to display a telephone number."  
  18.                 getnum  
  19.                 ;;  
  20.         "2")  
  21.                 echo "You want to add a new telephone number."  
  22.                 addnum  
  23.                 ;;  
  24.         "q")  
  25.                 flag="0"  
  26.                 ;;  
  27.         "Q")  
  28.                 flag="0"  
  29.                 ;;  
  30.         *)  
  31.                 echo "You made an invalid selection."  
  32.         esac  
  33. done  
5,程序friends.sh
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/bash  
  2. for friend in "Mary Jones" "Joe Smith" "Sue Jones"  
  3. do  
  4.         echo "Hello,$friend"  
  5. done  
6,程序raining.sh
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/bash  
  2. declare raining="1"  
  3. while [ $raining -eq "1" ]  
  4. do  
  5.         clear  
  6.         echo ""  
  7.         echo "Is it raining?"  
  8.         echo ""  
  9.         echo "1,YES"  
  10.         echo "2,NO"  
  11.         echo ""  
  12.         echo "Enter your selection:"  
  13.         read raining  
  14. done  
  15.   
  16. echo "It stopped raining."  
7,程序blink.sh
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/bash  
  2. clear  
  3. declare count1=1  
  4. declare count2  
  5.   
  6. while [ $count1 -lt 6 ]  
  7. do  
  8.         echo "Warning:There is a bug in your program!"  
  9.         let count2=1  
  10.         while [ $count2 -lt 20000 ]  
  11.         do  
  12.                 let count2="$count2 + 1"  
  13.         done  
  14.         clear  
  15.         let count2=1  
  16.         while [ $count2 -lt 20000 ]  
  17.         do  
  18.                 let count2="$count2 + 1"  
  19.         done  
  20.   
  21.         let count1="$count1 + 1"  
  22. done  
8,程序break.sh
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/bash  
  2. let n=1  
  3. while [ $n -eq 1 ]  
  4. do  
  5.         echo "Enter your name or \"stop\" to end:"  
  6.         read name  
  7.         case $name in  
  8.         "stop")  
  9.                 echo "Bye!"  
  10.                 break  
  11.                 ;;  
  12.         *)  
  13.                 echo "Hi,$name!"  
  14.                 ;;  
  15.         esac  
  16. done  
9,程序function_dis.sh
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/bash  
  2. clear  
  3. function display  
  4. {  
  5.         echo "Welcome to the world"  
  6.         echo "of functions."  
  7. }  
  8.   
  9. display  
10,程序function_verify.sh
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #!/bin/bash  
  2. clear  
  3. function verify  
  4. {  
  5.         if [ $# -ne 2 ]  
  6.         then  
  7.                 echo "Wrong number of arguments!"  
  8.         #字符串的比较用=  而数字的比较用-eq  
  9.         elif [ $1 = "gexing111" ] && [ $2 = "111gexing" ]  
  10.         then  
  11.                 echo "Verified"  
  12.         else  
  13.                 echo "Rejected"  
  14.         fi  
  15. }  
  16. verify $1 $2  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值