1、Linux 操作系统,Shell 脚本编程,主要内容和学习要求,掌握创建 shell 脚本的基本步骤学会使用条件测试掌握 if 条件结构与 case 选择结构掌握 for 循环、while 循环和 until 循环结构学会 shift 命令的使用学会 shell 脚本的调试,Shell 脚本,Shell 脚本,如果有一系列你经常使用的Linux命令,你可以把它们存储在一个文件里,shell可以读取这个文件并顺序执行其中的命令,这样的文件被称为脚本文件。shell 脚本按行解释。,Shell 脚本的编写,Shell 脚本是纯文本文件,可以使用任何文本编辑器编写Shell 脚本通常是以 .sh 作
2、为后缀名,Shell 脚本的执行,chmod +x script_name ./script_name,bash script_name,第一行:指定用哪个程序来编译和执行脚本。,Shell 脚本的格式,#!/bin/bash,可执行语句和 shell 控制结构,注释:以 “ # ” 开头,可独占一行,或跟在语句的后面。,Shell 脚本,#!/bin/sh,#!/bin/csh,一个 shell 脚本通常由一组 Linux 命令、shell 命令、控制结构和注释语句构成。,在脚本中多写注释语句是一个很好的编程习惯,#!/bin/bash # This is the first Bash sh
3、ell program # ScriptName: greetings.sh echo echo e “Hello $LOGNAME, c“ echo “its nice talking to you.“ echo “Your present working directory is:“ pwd # Show the name of present directory echo echo e “The time is date +%T!. nBye“ echo,bash greetings.sh,chmod +x greetings.sh ./greetings,Shell 脚本举例,echo
4、命令,功能说明:显示文字。 语 法:echo -ne字符串或 echo -help-version 补充说明:echo会将输入的字符串送往标准输出。输出的字符串间以空白字符隔开, 并在最后加上换行号。 -n 不进行换行 -e 若字符串中出现以下字符,则特别加以处理,而不会将它当成一般文字输出 n 换行 b 空格.,参 数:,-n 不要在最后自动换行 -e 若字符串中出现以下字符,则特别加以处理,而不会将它当成一般文字输出: a 发出警告声; b 删除前一个字符; c 最后不加上换行符号; f 换行但光标仍旧停留在原来的位置; n 换行且光标移至行首; r 光标移至行首,但不换行; t 插入ta
5、b; v 与f相同; 插入字符; nnn 插入nnn(八进制)所代表的ASCII字符; -help 显示帮助 -version 显示版本信息,#!/bin/bash # This script is to test the usage of read # Scriptname: ex4read.sh echo “= examples for testing read =“ echo -e “What is your name? c“ read name echo “Hello $name“ echo echo -n “Where do you work? “ read echo “I gues
6、s $REPLY keeps you busy!“ echo read -p “Enter your job title: “#自动读给REPLY echo “I thought you might be an $REPLY.“ echo echo “= End of the script =“,Shell 脚本举例,read命令,read variable #读取变量给variableread x y #可同时读取多个变量read #自动读给REPLYread p “Please input: ” #自动读给REPLY,状态变量 $? 中保存命令退出状态的值,grep $USER /etc/
7、passwd echo $? grep hello /etc/passwd; echo $?,条件测试,条件测试可以根据某个特定条件是否满足,来选择执行相应的任务。,Bash 中允许测试两种类型的条件: 命令成功或失败,表达式为真或假,任何一种测试中,都要有退出状态(返回值),退出状态为 0 表示命令成功或表达式为真,非0 则表示命令失败或表达式为假。,内置测试命令 test,通常用 test 命令来测试表达式的值,x=5; y=10 test $x -gt $y echo $?,test 命令可以用 方括号 来代替,x=5; y=10 $x -gt $y echo $?,表达式测试包括字符串
8、测试、整数测试和文件测试。,测试表达式的值,方括号前后要留空格!,name=Tom $name = Tt? echo $?,2.x 版本以上的 Bash 中可以用双方括号来测试表达式的值,此时可以使用通配符进行模式匹配。,测试表达式的值, $name = Tt? echo $?,字符串测试,name=Tom; -z $name ; echo $?,操作符两边必须留空格!,字符串测试,name2=Andy; $name = $name2 ; echo $?,整数测试,即比较大小,x=1; $x -eq 1 ; echo $?,x=a; $x -eq 1 ; echo $?,整数测试,操作符两边必
9、须留空格!,X,整数测试也可以使用 let 命令或双圆括号,x=1; let “$x = 1“; echo $?,x=1; ($x+1= 2 ); echo $?,只能用于整数测试!,整数测试,相应的操作符为:,= 、!= 、 、= 、 、=,例:,两种测试方法的区别,使用的操作符不同let 和 双圆括号中可以使用算术表达式,而中括号不能let 和 双圆括号中,操作符两边可以不留空格,逻辑测试,x=1; name=Tom; $x -eq 1 a n $name ; echo $?,逻辑测试,注:不能随便添加括号, ( $x -eq 1 ) a ( n $name ) ; echo $?,X,x
10、=1; name=Tom; $x -eq 1 echo $?,可以使用模式的逻辑测试,逻辑测试,文件测试:文件是否存在,文件属性,访问权限等。,常见的文件测试操作符,更多文件测试符参见 test 的在线帮助,man test,文件测试,检查空值, “$name“ = “ , ! “$name“ , “X$name“ != “X“ ,检查空值,语法结构,if expr1 # 如果expr1 为真(返回值为0) then # 那么commands1 # 执行语句块 commands1 elif expr2 # 若expr1 不真,而expr2 为真 then # 那么commands2 # 执行语
11、句块 commands2. . # 可以有多个 elif 语句 else # else 最多只能有一个commands4 # 执行语句块 commands4 fi # if 语句必须以单词 fi 终止,if 条件语句,commands 为可执行语句块,如果为空,需使用 shell 提供的空命令 “ : ”,即冒号。该命令不做任何事情,只返回一个退出状态 0,if 语句可以嵌套使用,ex4if.sh,chkperm.sh,chkperm2.sh, name_grep,tellme,tellme2,idcheck.sh,几点说明,elif 可以有任意多个(0 个或多个),else 最多只能有一个(
12、0 个或 1 个),if 语句必须以 fi 表示结束,expr 通常为条件测试表达式;也可以是多个命令,以最后一个命令的退出状态为条件值。,ex4if.sh,#!/bin/bash # scriptname: ex4if.sh # echo -n “Please input x,y: “ read x y echo “x=$x, y=$y“ if ( x y ); thenecho “x is larger than y“ elif ( x = y); thenecho “x is equal to y“ elseecho “x is less than y“ fi,chkperm.sh,#!
13、/bin/bash # Using the old style test command: # filename: perm_check.sh # file=testing if -d $file thenecho “$file is a directory“ elif -f $file then if -r $file -a -w $file -a -x $file then # nested if commandecho “You have read,write,and execute permission on $file.“fi elseecho “$file is neither a
14、 file nor a directory. “ fi,chkperm2.sh,#!/bin/bash # Using the new style test command: # filename: perm_check2.sh # file=./testing if -d $file thenecho “$file is a directory“ elif -f $file then if -r $file & -w $file & -x $file then # nested if commandecho “You have read,write,and execute permissio
15、n on $file.“fi elseecho “$file is neither a file nor a directory. “ fi,name_grep,#!/bin/bash # filename: name_grep # name=Tom if grep “$name“ /etc/passwd & /dev/null then: elseecho “$name not found in /etc/passwd“exit 2 fi,tellme,#!/bin/bash echo -n “How old are you? “ read age if $age -lt 0 -o $age
16、 -gt 120 thenecho “Welcome to our planet! “exit 1 fi if $age -ge 0 -a $age -le 12 thenecho “Children is the flowers of the country“ elif $age -gt 12 -a $age -le 19 thenecho “Rebel without a cause“ elif $age -gt 19 -a $age -le 29 then echo “You got the world by the tail!“ elif $age -ge 30 -a $age -le
17、 39 thenecho “Thirty something.“elseecho “Sorry I asked“ fi,tellme2,#!/bin/bash echo -n “How old are you? “ read age if ( age 120 ) thenecho “Welcome to our planet! “exit 1 fi if (age = 0 & age = 13 & age = 19 & age = 30 & age = 39 ) thenecho “Thirty something.“ elseecho “Sorry I asked“ fi,idcheck.s
18、h,#!/bin/bash # Scriptname: idcheck.sh # purpose: check user id to see if user is root. # Only root has a uid of 0. # Format for id output: uid=501(tt) gid=501(tt) groups=501(tt) # roots uid=0 : uid=0(root) gid=0(root) groups=0(root) # id=id | awk -F=( print $2 # get user id echo “your user id is: $
19、id“ if ( id = 0 ) # $id -eq 0 then echo “you are superuser.“ elseecho “you are not superuser.“ fi,语法结构,case expr in # expr 为表达式,关键词 in 不要忘!pattern1) # 若 expr 与 pattern1 匹配,注意括号commands1 # 执行语句块 commands1; # 跳出 case 结构pattern2) # 若 expr 与 pattern2 匹配commands2 # 执行语句块 commands2; # 跳出 case 结构 . . # 可以有
20、任意多个模式匹配*) # 若 expr 与上面的模式都不匹配commands # 执行语句块 commands; # 跳出 case 结构 esac # case 语句必须以 esac 终止,case 选择语句,case 语句举例:yes_no.sh,几点说明,每个命令块的最后必须有一个双分号,可以独占一行,或放在最后一个命令的后面。,所给的匹配模式 pattern 中可以含有通配符和“ | ”。,如果 expr 没有找到匹配的模式,则执行缺省值 “ *) ” 后面的命令块 ( 类似于 if 中的 else ); “ *) ” 可以不出现。,表达式 expr 按顺序匹配每个模式,一旦有一个模式
21、匹配成功,则执行该模式后面的所有命令,然后退出 case。,yes_no.sh,#!/bin/bash # test case # scriptname: yes_no.sh # echo -n “Do you wish to proceed y/n: “ read ans case $ans iny|Y|yes|Yes)echo “yes is selected“;n|N|no|No)echo “no is selected“;*)echo “basename $0: Unknown response“exit 1; esac,语法结构,for variable in list # 每一次循
22、环,依次把列表 list 中的一个值赋给循环变量 do # 循环开始的标志commands # 循环变量每取一次值,循环体就执行一遍 done # 循环结束的标志,几点说明,列表 list 可以是命令替换、变量名替换、字符串和文件名列表 ( 可包含通配符 )for 循环执行的次数取决于列表 list 中单词的个数for 循环体中一般要出现循环变量,但也可以不出现,for 循环语句,执行第一轮循环时,将 list 中的第一个词赋给循环变量,并把该词从 list 中删除,然后进入循环体,执行 do 和 done 之间的命令。下一次进入循环体时,则将第二个词赋给循环变量,并把该词从 list 中删除
23、,再往后的循环也以此类推。当 list 中的词全部被移走后,循环就结束了。,循环执行过程,forloop.sh,mybackup.sh,位置参量的使用: $* 与 $,greet.sh,可以省略 in list ,此时使用位置参量,permx.sh idcheck.sh greet.sh yes_no.sh permx.sh *.sh,for 循环执行过程,forloop.sh,#!/bin/bash # Scriptname: forloop.sh for name in Tom Dick Harry Joe doecho “Hi $name“ done echo “out of loop“
24、,forloop2.sh,#!/bin/bash # Scriptname: forloop2.sh for name in cat namelist doecho “Hi $name“ done echo “out of loop“,mybackup.sh,#!/bin/bash # Scriptname: mybackup.sh # Purpose: Create backup files and store # them in a backup directory. # backup_dir=backup mkdir $backup_dir for file in *.sh doif -
25、f $file thencp $file $backup_dir/$file.bakecho “$file is backed up in $backup_dir“fi done,greet.sh,#!/bin/bash # Scriptname: greet.sh # usage: greet.sh Tom John Anndyecho “= using $* =“ for name in $* # same as for name in $ doecho Hi $name done echo “= using $ =“ for name in $ # same as for name in
26、 $* doecho Hi $name done echo = using “$*“ = for name in “$*“ doecho Hi $name done echo = using “$“ = for name in “$“ doecho Hi $name done,permx.sh,#!/bin/bash # Scriptname: permx.sh # for file # Empty wordlist doif -f $file & ! -x $file thenchmod +x $fileecho “ = $file now has execute permission“fi
27、 done,语法结构,while expr # 执行 expr do # 若 expr 的退出状态为0,进入循环,否则退出whilecommands # 循环体 done # 循环结束标志,返回循环顶部,执行过程,先执行 expr,如果其退出状态为 0,就执行循环体。执行到关键字 done 后,回到循环的顶部,while 命令再次检查 expr 的退出状态。以此类推,循环将一直继续下去,直到 expr 的退出状态非 0 为止。,while 循环语句,语法结构,until expr # 执行 expr do # 若expr的退出状态非0,进入循环,否则退出untilcommands # 循环体
28、done # 循环结束标志,返回循环顶部,执行过程,与 while 循环类似,只是当 expr 退出状态非 0 时才执行循环体,直到 expr 为 0 时退出循环。,until 循环语句,用于强行退出当前循环。如果是嵌套循环,则 break 命令后面可以跟一数字 n,表示退出第 n 重循环(最里面的为第一重循环)。,用于忽略本次循环的剩余部分,回到循环的顶部,继续下一次循环。如果是嵌套循环,continue 命令后面也可跟一数字 n,表示回到第 n 重循环的顶部。,break n,continue n,例:months.sh,break 和 continue,months.sh,#!/bin/
29、bash # Scriptname: months.sh for month in Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec dofor week in 1 2 3 4 doecho -n “Processing the month of $month. OK? “read ansif “$ans“ = n -o -z “$ans“ thencontinue 2else,echo -n “Process week $week of $month? “read ansif “$ans“ = n -o -z “$ans“ thencontinu
30、eelseecho “Now processing week $week of $month.“sleep 1 # Commands go hereecho “Done processing.“fifidone done,sleep n,exit 和 sleep,exit n,exit 命令用于退出脚本或当前进程。n 是一个从 0 到 255 的整数,0 表示成功退出,非零表示遇到某种失败而非正常退出。该整数被保存在状态变量 $? 中。,exit 命令,sleep 命令,暂停 n 秒钟,语法结构,说明,select 循环主要用于创建菜单,按数字顺序排列的菜单项将显示在标准错误上,并显示 PS3
31、 提示符,等待用户输入用户输入菜单列表中的某个数字,执行相应的命令用户输入被保存在内置变量 REPLY 中。,select variable in list do # 循环开始的标志commands # 循环变量每取一次值,循环体就执行一遍 done # 循环结束的标志,例:runit.sh,select 循环与菜单,runit.sh,#!/bin/bash # Scriptname: runit.shPS3=“Select a program to execute: “ select program in ls -F pwd date do $program done,例:goodboy.s
32、h,select 经常和 case 联合使用,select 是个无限循环,因此要记住用 break 命令退出循环,或用 exit 命令终止脚本。也可以按 ctrl+c 退出循环。,与 for 循环类似,可以省略 in list ,此时使用位置参量,select 与 case,goodboy.sh,#!/bin/bash # Scriptname: goodboys.sh PS3=“Please choose one of the three boys : “ select choice in tom dan guy #select choice do case $choice intom)ec
33、ho Tom is a cool dude!break; # break out of the select loopdan | guy )echo Dan and Guy are both wonderful.break;*) echo “$REPLY is not one of your choices“echo “Try again.“;esac done,shift n,用于将参量列表 list 左移指定次数,缺省为左移一次。参量列表 list 一旦被移动,最左端的那个参数就从列表中删除。while 循环遍历位置参量列表时,常用到 shift。,./doit.sh a b c d e
34、f g h,./shft.sh a b c d e f g h,循环控制 shift 命令,例:,doit.sh,#!/bin/bash # Name: doit.sh # Purpose: shift through command line arguments # Usage: doit.sh args while ( $# 0 ) # or $# -gt 0 doecho $*shift done,shft.sh,#!/bin/bash # Using shift to step through all the positional parameters.until -z “$1“ #
35、Until all parameters used up. doecho “$1“shift done echo # Extra line feed. exit 0,生成随机数的特殊变量,echo $RANDOM 范围是: 0, 32767,expr:通用的表达式计算命令,表达式中参数与操作符必须以空格分开,表达式中的运算可以是算术运算,比较运算,字符串运算和逻辑运算。,expr 5 % 3,expr 5 * 3 # 乘法符号必须被转义,随机数和 expr 命令,字符串操作,注:pattern,old 中可以使用通配符。,例:ex4str,m 的取值从 0 到 $#var-1,字符串操作,ex
36、4str,#!/bin/bash dirname=“/usr/bin/local/bin“; echo “dirname=$dirname“ echo -n $#dirname=; sleep 4;echo “$#dirname“ echo echo -n $dirname:4=; sleep 4;echo “$dirname:4“ echo echo -n $dirname:8:6=; sleep 4; echo $dirname:8:6 echo echo -n $dirname#*bin=; sleep 4; echo $dirname#*bin echo echo -n $dirnam
37、e#*bin=; sleep 4;echo $dirname#*bin echo echo -n $dirname%bin=; sleep 4;echo $dirname%bin echo echo -n $dirname%bin=; sleep 4;echo $dirname%bin echo echo -n $dirname%bin*=; sleep 4;echo $dirname%bin* echo echo -n $dirname%bin*=; echo $dirname%bin* echo echo -n $dirname/bin/sbin=; echo $dirname/bin/s
38、bin echo echo -n $dirname/bin/lib=; echo $dirname/bin/lib echo echo -n $dirname/bin*/lib=; echo $dirname/bin*/lib,sh x 脚本名,该选项可以使用户跟踪脚本的执行,此时 shell 对脚本中每条命令的处理过程为:先执行替换,然后显示,再执行它。 shell 显示脚本中的行时,会在行首添加一个加号 “ + ”。,sh v 脚本名,在执行脚本之前,按输入的原样打印脚本中的各行, 打印一行执行一行。,sh n 脚本名,对脚本进行语法检查,但不执行脚本。如果存在语法错误,shell 会报错
39、,如果没有错误,则不显示任何内容。,脚本调试,编程小结:变量,局部变量、环境变量(export、declare -x),只读变量、整型变量,例:declare -i x; x=“hello“; echo $x,0,位置参量($0,$1,.,$*,$,$#,$,$?),变量的间接引用(eval, $!str),例:name=“hello“; x=“name“; echo $!x,hello,命令替换(cmd、$(cmd)),整数运算declare 定义的整型变量可以直接进行运算, 否则需用 let 命令或 $.、$(.) 进行整数运算。,编程小结:输入输出,输入:read,read var1 v
40、ar2 .,read,read p “提示“,输出:printf,printf “%-12.5f t %d n“ 123.45 8,format 以%开头,flag,field width,precision,格式符,-:左对齐 +:输出符号 0:空白处添0 空格:前面加一空格,字段宽度,小数点后输出位数,c d e f g s o x,b n r t v ” %, REPLY, REPLY,输出参数用空格隔开,字符串测试,编程小结:条件测试,操作符两边必须留空格!,如果使用双方括号,可以使用 通配符 进行模式匹配。,例:name=Tom; $name Tom ; echo $?,编程小结:条
41、件测试,整数测试,注意这两种方法的区别!,编程小结:条件测试,逻辑测试,如果使用双方括号,可以使用 通配符 进行模式匹配。,编程小结:条件测试,文件测试,编程小结:控制结构,if 条件语句case 选择语句for 循环语句while 循环语句until 循环语句break、continue、sleep 命令select 循环与菜单shift 命令,$.,$(.),$.,$(.).,.,(.),各种括号的作用,function_name () commands ,函数,一个函数就是一个子程序,用于完成特定的任务,当有重复代码,或者一个任务只需要很少的修改就被重复几次执行时, 这时你应考虑使用函数
42、。,function function_name commands ,函数的一般格式,和其它编程语言一样, Bash 也可以定义函数。,函数举例,#!/bin/bashfun1 () echo “This is a function“ echo “Now exiting fun1.“ fun2 () echo “This is fun2.“echo “Now exiting fun2.“ ,只需输入函数名即可调用该函数。,函数的调用,函数必须在调用之前定义,#!/bin/bashfun2 () echo “This is fun2.“echo “Now exiting fun2.“ fun2
43、# 调用函数 fun2,例:ex4fun2.sh, ex4fun3.sh,ex4fun2.sh,#!/bin/bash JUST_A_SECOND=1 fun () # A somewhat more complex functioni=0REPEATS=5echoecho “And now the fun really begins.“echosleep $JUST_A_SECOND # Hey, wait a second!while $i -lt $REPEATS doecho “-FUNCTIONS-“echo “echolet “i+=1“done # Now, call the f
44、unctions. fun exit 0,ex4fun3.sh,# f1 # Will give an error message, since function “f1“ not yet defined.# declare -f f1 # This doesnt help either. # f1 # Still an error message.# However. f1 () echo “Calling function “f2“ from within function “f1“.“ f2 f2 () echo “Function “f2“.“ # f1 # Function “f2“
45、 is not actually called until this point # although it is referenced before its definition. # This is permissible.,向函数传递参数,函数的调用,例:ex4fun4.sh,函数与命令行参数,例:ex4fun5.sh,return 与 exit,例:ex4fun6.sh,向函数传递参数 例:ex4fun4.sh,#!/bin/bash # Functions and parametersDEFAULT=default # Default param value. func2 () if
46、 -z “$1“ # Is parameter #1 zero length?thenecho “-Parameter #1 is zero length -“ elseecho “-Param #1 is “$1“ -“ fivariable=$1:-$DEFAULT echo “variable = $variable“ if -n “$2“ thenecho “- Parameter #2 is “$2“ -“ fi return 0 ,echo echo “Nothing passed“ func2 # Called with no paramsecho echo “One param
47、eter passed.“ func2 first # Called with one paramecho echo “Two parameters passed.“ func2 first second # Called with two paramsecho echo “ “second“ passed.“ func2 “ second # The first parameter is of zero?lengthecho exit 0 # End of script,函数与命令行参数 例:ex4fun5.sh,#!/bin/bash # function and command line
48、 arguments # Call this script with a command line argument, # something like $0 arg1.func () echo “$1“ echo “First call to function: no arg passed.“ echo “See if command-line arg is seen.“ Func # No! Command-line arg not seen. echo “=“ echo echo “Second call to function: command-line arg passed explicitly.“ func $1 # Now its seen! exit 0,