linuxshell编程入门教程学习

【十二】shell 编程
12.1 Shell 环境概述
Shell 的作用:命令解释器,“翻译官” 。介于操作系统内核与用户之间,负责解释命令行 。

linuxshell编程入门教程学习

文章插图
Shell 环境的切换:
1)登录Shell
/etc/shells 文件记录了系统支持的有效登录Shell
[root@alex ~]# cat /etc/shells2)切换Shell环境
临时切换:直接执行其他Shell程序,示例ksh、zsh等 。
更改用户登录Shell:
需修改 /etc/passwd 文件中用户记录的最后一个字段
或执行:usermod -s Shell程序路径 用户名
3)查看缺省的shell
[root@alex ~]# echo $SHELL/bin/bash
【Bash的历史命令】
1)保存用户曾经执行过的命令操作
存放位置:~/.bash_history 文件
2)查看历史命令
3)执行:history
清除历史命令history -c
4)调用历史命令
!n:执行历史记录中的第n条命令
!str:执行历史记录中最近一次以“str”开头的命令【这个有用】
设置历史命令能够输出的记录数,修改 HISTSIZE 参数(默认为1000条) 【这个环境变量在/etc/profile里】 。
Bash的命令别名:为使用频率较高的复杂命令行设置简短的调用名称
存放位置:~/.bashrc
查看命令别名:格式:alias [别名]
如:alias sqlplus='rlwrap sqlplus'取消已设置的命令别名 格式:unalias 别名
【注】:.bashrc会被.bash_profile调用,所以也可以将别名写到.bash_profile中 。
11.2 Shell变量应用
变量的种类:
Shell变量是用来代表某个值的符号名,变量是shell传递数据的一种方法 。为灵活管理,Linux系统提供特定参数 , 有两层意思:
变量名:使用固定的名称,由系统预设或用户定义
变量值:能够根据用户设置、依系统环境变化而变化
Shell变量的种类:
1)用户自定义变量:由用户自己定义、修改和使用
2)环境变量:由系统维护,用于设置用户的Shell工作环境,只有极少数的变量用户可以修改
3)预定义变量:Bash预定义的特殊变量,不能直接修改
4)位置变量:通过命令行给脚本传递执行参数
用户自定义变量:
定义新的变量:变量名要以英文字母或下划线开头,区分大小写
格式:变量名=变量值
查看变量的值:
格式:echo $变量名
在使用变量值时 , 要在变量名前加上前缀“$” 。
示例①:
[root@alex ~]# abc='hello world' 字符串中有空格要使用引号[root@alex ~]# echo $abchello world
[root@alex ~]# unset abc 删除变量变量值可以作为某个长字符串中的一部分 。如果它在长字符串的末尾,就可以利用直接引用形式,如果变量的值须出现在长字符串的开头或中间,避免shell把它与其他字符混在一起,则应该用花括号将变量名括起来 。
示例②:
[root@alex ~]# abc=china[root@alex ~]# echo ******
[root@alex ~]# echo ******
[root@alex ~]# echo ******
从键盘输入内容为变量赋值:
格式: read [-p “提示信息"] 变量名
结合不同的引号为变量赋值:
双引号 “ ” :允许通过$符号引用其他变量值
单引号 ‘ ’ :禁止引用其他变量值 , $视为普通字符
示例③
[root@alex ~]# vi alex1.sh#!/bin/bashx=abcprintf "x is now $x. Enter new value: " $xread xecho $x【注】:printf是一个函数
验证:
[root@alex ~]# sh alex1.shx is now abc. Enter new value: xyz
xyz
倒引号 ` ` :将命令执行的结果输出给变量
示例④:
[root@alex ~]# find /dev -type p/dev/initctl
[root@alex ~]# ls -l `find /dev -type p`prw——- 1 root root 0 01-30 13:02 /dev/initctl
【环境变量】
Shell有两类变量:临时变量和全局变量
临时变量是shell程序内部定义的,其使用范围仅限于定义它的程序,对其它程序不可见 。包括:用户自定义变量、位置变量和预定义变量 。
全局变量是环境变量,其值不随shell脚本的执行结束而消失 。把一个Shell变量用EXPORT命令导出,就创建了环境变量 。它们对于以后在该Shell下执行的所有程序都是可见的 。
设置环境变量PATH:
linuxshell编程入门教程学习

文章插图
常用命令的目录放在PATH变量中,使用频度高命令的目录排在前面;尽量避免查询大目录,如需设置,将其路径放在PATH路径的最后位置 。
例:PATH=/bin:/usr/bin:/etc:/:.位置变量(参数)
在执行Shell脚本时,可以定义最多9个位置参数 , 表示为$n,n为1~9之间的数字 。
linuxshell编程入门教程学习

文章插图
预定义变量:表示形式如下 。
$#:命令行中位置参数的数量
$*:所有位置参数的内容
$?:上一条命令执行后返回的状态,当返回状态值为0时表示执行正常,非0值表示执行异常或出错
$$:当前所在进程的进程号
$!:后台运行的最后一个进程号
$0:当前执行的进程/程序名
linuxshell编程入门教程学习

文章插图
【Shell中的通配符】:
1)* 匹配任何字符串 。
2)?匹配任何单个字符 。
3)[…,-]匹配方括号所限定的任何一个字符【最终是方括符中某单个字符满足要求】
示例:[a-d,x,y]是匹配单个字符【a、b、c、d、x、y单个字符满足要求】
[!Z] 是匹配不是Z的所有单个字符
4) 转意符,使原字符失去其特殊的含义 。
基本的算数运算:计算整数表达式的运算结果
格式:expr 变量1 运算符 变量2 …[运算符 变量n]
expr的常用运算符:
加法运算:+
减法运算: –
乘法运算: *
除法运算: /
求模(取余)运算: %
[root@alex ~]# a=1[root@alex ~]# b=2[root@alex ~]# c=`expr $a + $b` 运算符号两端要空格[root@alex ~]# echo $c3
[root@alex ~]# c=`expr ( $a + $b ) * $b` 运算符号两端要空格,并且()和*都要使用转意符“”[root@alex ~]# echo $c6
使用let命令格式更友好,但有些Shell不支持 。
[root@alex ~]# let c=a+b[root@alex ~]# echo $c3
[root@alex ~]# let c=(a+b)*b[root@alex ~]# echo $c6
【逻辑操作符】:
1)逻辑与&&:可以把两个命令联系在一起
形式:命令1 && 命令2
功能:先运行命令1,如果成功,才运行命令2,否则不运行命令2
2)逻辑或||:命令互补
形式:命令1 || 命令2
功能:先运行命令1,不成功运行命令2,否则不运行命令2.
试比较下面的例子:
[root@alex ~]# ls -l;date;uptime;LS;ls[root@alex ~]# ls -l&&date&&uptime&&LS&&ls[root@alex ~]# ls -l||date||uptime||LS||ls[root@alex ~]#LS||date||uptime||LS||ls LS不成功,补date【成组命令】
在shell中可以使用2种方式将若干命令组合在一起,只返回一个逻辑结果 。
使用花括号{}
使用圆括号()
以花括号括起来的命令可视为语法上的一条命令 。成组命令的执行顺序是根据命令出现的先后次序,由左向右执行 。
在使用花括号时在格式上应注意,左括号“{”后面应有一个空格;右括号“}”之前应有一个分号“;” 。
在使用(){} 也可以包含若干单独占一行的命令 。
[root@alex ~]# vi alex2.sh#!/bin/bashParent="P"echo Before: $Parent{Parent="C"echo After: $Parent}echo After: $Parent验证:
[root@alex ~]# sh alex2.shBefore: P
After: C
After: C
把{}换成()后
[root@alex ~]# sh alex2.shBefore: P
After: C
After: P
用圆括号括起来的成组命令是在新的子shell内执行 , 由于不属于同一进程,因此,在圆括号内的命令不会改变父 shell的变量值及工作目录 。
12.3 编写并执行Shell脚本(一)
【Shell脚本概念】:
用途:完成特定的、较复杂的系统管理任务
格式:集中保存多条Linux命令,普通文本文件
执行方式:按照预设的顺序依次解释执行 。
【脚本文件的内容】:
运行环境设置:#!/bin/bash 这句不是注释 , 它告诉系统后面的shell要用bash解释
注释信息:以#开始的说明性文字,可执行的Linux命令行 。
【脚本的可执行权限】:
1)直接执行具有“x”权限的脚本文件
示例:./repboot.sh
2)使用指定的解释器程序执行脚本内容
示例:bash repboot.sh、sh repboot.sh
3)通过source命令(或 . )读取脚本内容执行
示例:souce repboot.sh 或 . Hello.sh
12.4 编写并执行Shell脚本(二)
12.4.1条件测试操作
【测试命令test】
用途:测试特定的表达式是否成立,当条件成立时,命令执行后的返回值为0,否则为其他数值
格式:test 条件表达式
[ 条件表达式 ]
常见的测试类型:①测试文件状态②整数值比较③字符串比较④逻辑测试
条件测试-测试文件
测试文件状态
格式:[ 操作符 文件或目录 ]
常用的测试操作符:
-d:测试是否为目录(Directory)-e:测试目录或文件是否存在(Exist)-f:测试是否为文件(File)-r:测试当前用户是否有权限读?。≧ead)-w:测试当前用户是否有权限写入(Write)-x:测试当前用户是否可执行(Excute)该文件-L:测试是否为符号连接(Link)文件条件测试-整数值比较
整数值比较
格式:[ 整数1 操作符 整数2 ]
常用的测试操作符
-eq:等于(Equal)
-ne:不等于(Not Equal)
-gt:大于(Greater Than)
-lt:小于(Lesser Than)
-le:小于或等于(Lesser or Equal)
-ge:大于或等于(Greater or Equal)
条件测试-字符串比较
字符串比较
格式:[ 字符串1 = 字符串2 ]
[ 字符串1 != 字符串2 ]
[ -z 字符串 ]
常用的测试操作符
=:字符串内容相同
!=:字符串内容不同,! 号表示相反的意思
-z:字符串内容为空
条件测试—逻辑测试
逻辑测试
格式:[ 表达式1 ] 操作符 [ 表达式2 ] …
常用的测试条件的逻辑操作符
!逻辑非(NOT),它放在任意逻辑表达式之前,使原来的表达式由真变假,或者由假变真 。
例如:[ ! -r $1 ] 另一种写法!test -r “$1” 这句相当于说$1的内容非可读才是真
-a或&&:逻辑与 , “而且”的意思,
前后两个表达式都成立时整个测试结果才为真,否则为假
例如:[ -f “$myfile” -a -r “$myfile” ] $myfile即是文件又可读才是真,否则为假
-o或||:逻辑或,“或者”的意思
操作符两边至少一个为真时,结果为真,否则结果为假
例如:[ “$a” -ge 0 -o “$b” -le 100] 假如$a=5整个结果就是真了 。有一个为真就是真
()圆括号,把一个逻辑表达式括起来,使之优先得到运算,缺省下-a 的优先级高于-o,()可以改变这种优先级
12.4.2使用if条件语句
通常,if的测试部分是利用test命令或[]实现的 。其实,条件测试可以利用一般命令执行成功与否来作判断 。如果命令正常结束,则表示执行成功,其返回值为0 , 条件测试为 真,如果命令执行不成功,其返回值不等于0,条件测试就为假 。如果,各命令表可以由一条或者多条命令组成,那么测试条件以其最后一条命令是否执行成功为准 。
单分支:当“条件成立”时执行相应的操作
linuxshell编程入门教程学习

文章插图
双分支:当“条件成立”、“条件不成立”时执行不同操作
linuxshell编程入门教程学习

文章插图
多分支:相当于if语句嵌套,针对多个条件执行不同操作
linuxshell编程入门教程学习

文章插图
示例:
[root@alex ~]# vi finduser.sh#!/bin/bashif [ $# -ne 1 ]thenecho Usage: please input single username >&2exit 1fiwho | grep $1【注】:>&2表示把标准输出和错误输出都定向到终端上
[root@alex ~]# bash finduser.sh root 一个位置参数root pts/0 2016-02-01 08:52 (192.168.3.100)
[root@alex ~]# bash finduser.sh root oracle 两个位置参数时没有结果Usage: finduser username
[root@alex ~]# echo $?1 exit返回值是1
12.4.3使用循环语句
一)For循环
根据变量的不同取值,重复执行一组命令操作
linuxshell编程入门教程学习

文章插图
For语句有三种格式 , 不同在于in的后面可以跟
For 变量 in 值表
For 变量 in 文件正则表达式
For 变量 in 命令行的位置参数
示例1:for语句使用值表 。计算1 3 5 7 9的和;并且输出当前目录下的所有.sh文件 。
[root@alex ~]# vi forapp1.sh#!/bin/bashresult=0for i in 1 3 5 7 9dolet result=result+idoneecho "result = $result"###j=1for file in *.shdoecho "The ${j}th file is: $file"j=`expr $j + 1`done验证
[root@alex ~]# bash forapp1.sh【linuxshell编程入门教程学习】示例2:for语句使用位置参数 。显示所有位置参数
[root@alex ~]# vi forapp2.sh#!/bin/bashj=1for i in $*doecho "The ${j}th parameter is: $i"let j=j+1done验证
[root@alex ~]# bash forapp2.sh p1 p2The 1 th parameter is: p1
The 2 th parameter is: p2
二)While循环语句
重复测试指定的条件,只要条件成立则反复执行对应的命令操作
linuxshell编程入门教程学习

文章插图
示例1:whileapp1脚本:求1到10的和
[root@alex ~]# vi whileapp1.sh#!/bin/bashx=1result=0while [ $x -le 10 ]dolet result=result+xlet x=x+1doneecho $result验证
[root@alex ~]# sh whileapp1.sh55
示例2:
批量添加20个系统用户帐号,用户名依次为“stu1”、“stu2”、……、“stu20” , 这些用户的初始密码均设置为“123456”
#vi adduser.sh#!/bin/bashi=1while [ $i -le 20 ]douseradd stu$iecho "123456" | passwd --stdin stu$i &> /dev/nulli=`expr $i + 1`done应用示例2:批量删除上例中添加的20个系统用户帐号
# vi deluser.sh i#!/bin/bashi=1while [ $i -le 20 ]douserdel -r stu$ii=`expr $i + 1`done三)Until循环
until语句根据条件执行重复操作
形式:
Until 测试条件
do
命令表
done
它与while语句很相似,只是测试条件不同;当测试条件为假时,才进入循环体,直至测试条件为真时终止循环 。
示例:
1)添加一个tim用户,并授予密码
[root@alex ~]# useradd tim[root@alex ~]# passwd tim2)编写untilapp1.sh脚本:等待某个用户(tim)登录,每20秒确定一次
[root@alex ~]#vi untilapp1.sh#!/bin/bashprintf "Enter username: "read useruntil who | grep $user > /dev/nulldosleep 20doneecho "$user have logged in"3)执行该脚本会一直处于执行状态
[root@alex ~]#sh untilapp1.shEnter username: tim
4)在主控台让tim登录 , 则该脚本才会执行完成 。
Enter username: tim
tim have logged in
12.4.4 CASE语句
CASE语句
根据变量的不同取值,分别执行不同的命令操作,Case语句允许进行多重条件选择 。语法形式如下:
case 字符串 in
正则表达式1)命令
命令;;
正则表达式2)命令
命令;;
正则表达式n)命令
命令;;
esac
使用case语句应注意:
1)每个正则表达式后面可以有一条或多条命令,其最后一个命令必须以;结束,Exit命令后可以不要 。
2)正则表达式中可以使用通配符 。
3)如果一个正则表达式是由多个模式组成,那么各模式之间应以竖线 (|)隔开,表示各模式是“或”的关系,即只要给定字符串与其中一个模式相配 , 就会执行其后的命令表 。
4)各正则表达式应是唯一的,不应重复出现 。并且要合理安排正则表达式的出现顺序 。例如,不应将“*”作为头一个正则表达式 。因为与任何字符串匹配,它若第一个出现,就不会再检查其他表达式了 。
5)Case的退出(返回)值是整个结构中最后执行的那个命令 。若没有执行任何命令,则退出值为零 。
示例:
[root@alex ~]# vi caseapp.sh#!/bin/bashname=`basename $0 .sh`case $1 ins|start)echo "start...";;stop)echo "stop...";;reload)echo "reload...";;*)echo "Usage: $name [start|stop|reload]"exit 1;;esacexit 0【注】:basename这里就是取得不带路径的执行的shell脚本的名称, 扩展名.sh也会去掉 。
例如:# basename /root/test/finduser.sh .sh
finduser
验证
[root@alex ~]# sh caseapp.sh sstart…
[root@alex ~]# sh caseapp.sh startstart…
[root@alex ~]# sh caseapp.sh stopStop…
[root@alex ~]# echo $?0
[root@alex ~]# sh caseapp.sh abcUsage: caseapp [start|stop|reload]
[root@alex ~]# sh caseapp.shUsage: caseapp [start|stop|reload]
[root@alex ~]# echo $?1
SHIFT 语句:
位置参数最多不能超过9个,即$1~$9 。如果实际给定的 命令行参数多于9个,就需要用shift命令移动位置参数 。每执行一次 shift 命令,就把位置参数向左移一位,新的$1的值是原来$2的值,新 $2 是原来$3的值,依次类推 。
Shift 命令不能把$0移走 。Shift 命令可以带有一个整体作为参数 。如果没有带参数默认是1
示例:
[root@alex ~]# vi shiftapp1.sh#!/bin/bash#loop=0while [ $# -ne 0 ]doecho $1done# 修改权限,让其可执行
[root@alex ~]# chmod u+x shiftapp1.sh# 执行
[root@alex ~]# ./shiftapp1.sh p1 p2 p3结果:死循环,而不是将所有的参数输出后结束 。
#!/bin/bash#loop=0while [ $# -ne 0 ]do echo $1shiftdone[root@alex ~]# ./myapp1.sh p1 p2 p3p1
p2
p3…
如果改成shift 2如何
【循环控制语句】:break语句
在for、while、until等循环语句中,用于跳出当前所在的循环体,执行循环体后的语句
continue
在for、while、until等循环语句中,用于跳过循环体内余下的语句,重新判断条件以便执行下一次循环
Shell函数应用:
在编写Shell脚本程序时 , 将一些需要重复使用的命令操作,定义为公共使用的语句块,即可称为函数,合理使用Shell函数,可以使脚本内容更加简洁,增强程序的易读性,提高执行效率 。
linuxshell编程入门教程学习

文章插图
应用示例:
在脚本中定义一个加法函数,名叫adder,用于计算2个整数的求和
调用该函数计算(12+34)、(56+789)的和
[root@alex ~]# vi addderfun.sh#!/bin/bashadder() {echo `expr $1 + $2`}adder 12 34adder 56 789验证
[root@alex ~]# sh adderfun.sh46
845

the end ?。。?
@jackman 共筑美好!