千家信息网

Bash Shell脚本编程笔记总结(一)

发表于:2024-10-28 作者:千家信息网编辑
千家信息网最后更新 2024年10月28日,本文是上课笔记总结,涉及细节知识点会在以后文章说明!bash脚本编程:脚本程序:解释器解释执行;shell: 交互式接口;编程环境;shell: 能够提供一些内部命令,并且能通过PATH环境变量找到外
千家信息网最后更新 2024年10月28日Bash Shell脚本编程笔记总结(一)


本文是上课笔记总结,涉及细节知识点会在以后文章说明!

bash脚本编程:

脚本程序:解释器解释执行;


shell: 交互式接口;编程环境;

shell: 能够提供一些内部命令,并且能通过PATH环境变量找到外部命令;把命令提交给内核启动为进程;


编程环境:

流程控制语句:

顺序执行;

循环执行;

选择执行;


条件测试:真、假

$?

命令的状态结果:

0: 真

1-255: 假


过程式的编程语言的元素:变量、流程、函数、数组


变量:局部变量、本地变量、环境变量、位置参数变量、特殊变量

变量:

数值型:整型、浮点型、布尔型

字符型:字符串


bash变量是弱类型;默认字符型;


变量引用:${VAR_NAME}

引号:

弱引用:""

强引用:''

命令引用:``


声明某变量为整型变量:

let VAR_NAME=VALUEdeclare -i VAR_NAME=VALUE


声明某变量为环境变量:

export VAR_NAME=VALUEdeclare -x VAR_NAME=VALUE


脚本的编写格式:

第一行:写明解释器; #!/bin/bash

注释行:所有以#开头的行均为注释行;会被解释器忽略;


执行脚本:

赋予执行权限;指明路径执行;

直接传递脚本给bash解释器


bash的选项:

-n: 测试脚本中是否有语法 错误;

-x: 调试执行;


算术运算:

$[EXPRESSION]let VAR_NAME=EXPRESSION$((EXPRESSION))$(expr argu1 argu2 argu3)


流程控制之一:for循环

将一段代码反复执行;

表达式:

for VAR in LIST; doSTATEMENT1...done


for VAR in LIST; do statement1; statement2; ...; done


for ((初始化循环控制变量;循环条件;修正表达式)); do循环体done


进入条件; 退出条件;

循环体;STATEMENT1

睡眠:sleep [n]

循环次数:为列表中的元素的个数;

LIST:列表,包含至少一个元素的字符串集合;

(1) 直接给出;

(2) 数值列表:

(a) {start..end}

例如:{1..10}

(b) seq [start [step]] end

seq LASTseq FIRST LASTseq FIRST STEP LAST

(3) 返回列表的命令;

(4) globbing;

(5) 变量引用;

$*, $@

例如:添加3个用户,user1, user2, user3; 密码同用户名;

#vi useradd.sh#!/bin/bashfor username in user1 user2 user3; douseradd $usernameecho $username |passwd --stdin $usernamedone


例如:添加9个用户,user101...user109;密码同用户名;

#vi useradd2.sh#!/bin/bashfor i in {1..9}; douseradd user10$iecho user10$i |passwd --stdin user10$idone


练习:于/tmp/test目录中创建10个空文件f1,.., f10;

#vi addfile.sh#!/bin/bashdirectory=/tmp/test-$(date '+%Y%m%h')mkdir $directoryfor i in {1..10} ; dotouch $directory/f$idone

练习:写一个脚本,

(1) 创建"/tmp/test-当前时间"目录;

(2) 添加10用户tuser1,.., tuser10;

(3) 在"/tmp/test-当前时间"目录中,创建10空文件f1,...,f10;

(4) 修改f1的属主为tuser1;依次类推;

#vi creatdir.sh#!/bin/bashdir=/tmp/test-$(date '+%F-%H-%S')mkdir $dirfor i in {1..10} ; do        useradd testuser$i        touch $dir/f$i        chown testuser$i $dir/f$idonerm -rf $diruserdel testuser$i


练习:求100以内所有正整数之和;

#vi plus.sh#!/bin/bashsum=0for i in `seq 100`;dosum=$(($sum+$i))doneecho $sum


练习:求100以内所有偶数之和;以及所有奇数之和;

#!/bin/bashdeclare -i sum=0for i in `seq 1 2 100`;do        sum=$(($sum+$i))doneecho $sumdeclare -i sum1=0for i in `seq 2 2 100`; do        sum1=$(($sum1+$i))doneecho $sum1练习5:计算当前系统上所有用户ID之和;#!/bin/bashsum=0for i in `cut -d: -f3 /etc/passwd` ; do        sum=$(($sum+$i))doneecho $sum


练习:传递参数(文本文件路径)给脚本,统计所有文件的空白行数之和;显示此次共对多少文件进行统计;

#!/bin/bashsum=0num=0echo $#for i in `seq 1 $# `; do        num=`grep '^$' $1 |wc -l`        sum=$(($num+$sum))        shiftdoneecho  $sum


练习:显示当前系统所有默认shell为bash的用户的总数;并统计此些用户ID之和;

for id in `grep "/bin/bash$" /etc/passwd | cut -d: -f3`; do

#!/bin/bashsum=0num=0for i in `grep 'bash$' /etc/passwd |cut -d: -f3` ; do        sum=$(($sum+$i))        num=$(($num+1))done


for的几种特殊情况:

(1) for省略,会自动获取脚本参数列表;

(2) C编程风格:

for ((变量赋值;循环条件;修正表达式)); do

CMD1

CMD2

done


(3) 循环嵌套:

for i in [LIST1]; do

CMD1

for j in [LIST2]; do

CMD2

...

done

done


练习:写一个脚本

(1) ping 172.16.X.Y内的所有主机;

172.16.0-255.1-254


for i in {0..255}; do    for j in {1..254}; do        ping -c1 -w1 172.16.$i.$j    donedone


练习:写个脚本

(1) 传递一些目录给脚本;

(2) 逐个显示每个目录下的所有一级文件的内容类型;

(3) 统计一个有多少个目录;一共显示了多少个文件;


#!/bin/bash#declare -i dirs=0declare -i files=0for d in $*; do    for f in $d/*; do        file $f        let files++    done    let dirs++doneecho "Directories: $dirs."echo "Files: $files."



bash命令退出和退出状态码

命令在bash中执行结束退出时,其执行成功与否可通过退出状态码来记录

#echo $?


脚本的退出状态码取决于执行的最后一条命令;自定义退出状态码:

exit #

成功:0

失败:1-255

#!/bin/bashls /varrretval=$?echo 'hello'exit $retvel

以ls /varr的结果状态为退出状态

注意:提前退出脚本,也可使用exit命令实现


bash 脚本编程之条件测试:

if/then结构


条件测试:

test EXPRESSION[ EXPRESSION ][[ EXPRESSION ]]

COMMAND:使用的是命令退出状态码

1.使用命令执行结果

a.使用命令引用

b.使用比较操作符

例如:

[  `id -u $username ` -lt  500 ]userid=`id -u $username`[ $userid  -lt 500]

2.使用命令的退出状态码

a.先运行命令

b.引用状态码

引用方式两种:

1.if COMMAND &>/dev/null;then

引用的是命令运行的退出状态码

注意:COMMAND不能被使用命令引用,COMMAND的执行结果通常没有意义,所以其结果通常(&>)被定向至/dev/null

2.先执行命令,后判断退出状态码是否为0

COMMAND

retval=$?if [ $? -eq 0 ] ; then


测试表达式:

(1) 整数测试;例如:1<2

(2) 字符串测试;例如:比较字符串相等与否

(3) 文件测试;例如:判断文件类型


整数测试:A, B;需要双目操作符;

A -gt B: 大于

A -ge B: 大于和等于

A -eq B: 等于

A -lt B: 小于

A -le B: 小于等于

A -ne B: 不等于

符合为真,否则为假

注意:命令执行结果和状态返回结果不一样,需要判断。


字符串测试:A, B

A > B

A < B

A >= B

A <= B

A == B或A = B:等值比较

A != B: 不等于

-z A: 判断A是否为空;空则为真,不空则假;

-n A:判断A是否不空;不空则为值,空则为假;

[ $A = $B ]

"$A" =~ PATTERN

如果变量A中给定的字符串能被模式匹配即为真,否则为假

例如:过滤fdisk -l 结果的字符串

#disk=/dev/sda#[[ $dsk=~/dev/sd[a-z] ]]     变量和模式匹配#echo $?0 为真#disk=/dev/xda#[[ $disk =~ /dev/sd[a-z] ]]       变量和模式不匹配#echo $?1  为假


文件测试:$file

-e $file: 是否存在;存在则为真;

-a $file: 同上;弃用;

-f $file: 文件是否存在,且为普通文件;

-d $file: 是否存在且为目录;

-h $file: 是否存在且为符号链接文件;

-L $file:同上

-b $file: 是否存在且为块设备文件;

-c $file: 是否存在且为字符设备文件;

-S $file: 是否存在且为套接字文件:

-p $file: 是否存在且为管道文件;


-r $file: 当前用户对此文件是否拥有读权限;

-w $file: 写

-x $file: 执行权限;


-u $file: 文件是否拥有suid权限;

-g $file:文件是否拥有sgid权限;

-k $file: 文件是否拥有sticky权限;


-O $file: 当前用户是否为文件的属主;

-G $file: 当前用户是否属于文件的属组;


-N $file: 文件自从上一次被读取之后,是否被修改过;


$f1 -nt $f2: 文件f1是否比文件f2新;

$f1 -ot $f2: 文件f1是否比文件f2旧;

$f1 -ef $f2: f1和f2是否为同一个文件的硬链接;


练习:写一个脚本,传递一个文件路径参数给脚本

(1) 存在,则显示有此文件;

(2) 否则,则显示无此文件

#!/bin/bashif [ -e $1 ] ; thenecho "$1 exist"elseecho "$1 is not exist"fi


练习:写一个脚本,传递一个文件路径参数给脚本

(1) 如果脚本无参数,则显示必须给至少一个参数,退出脚本;退出码5;

(2) 路径指向的文件如果不存在,则直接退出脚本;退出码为6;

(3) 判断文件类型:

(a) 如果是普通文件,则显示为"common file";

(b) 如果是目录,则显示为"directory";

(c) 如果是符号链接,则显示为"symbolic link file";

(d) 如果是块设备,则显示为"block device file";

(e) 如果是字符设备,则显示为"character device file";

(f) 否则,则显示为"unkown";

#!/bin/bashif [ $# -lt 1 ] ; then        echo "you mast input a number"        exit 5 if [ ! -e $1 ] ; then        echo "the file is not exist"        exit 6if [ -f $1 ] ; then        echo "$1 is commonfile"elif [ -d $1 ] ; then        echo "$1 is directory"elif [ -L $1 ] ; then        echo "$1 is symbolic link file"elif [ -b $1 ] ; then        echo "$1 is block device file"elif [ -c $1 ] ; then        echo "$1 is character device file"else        echo "$1 is unknow"fi


练习:写一个脚本,其使用格式如下所示(其中所有的script.sh均为用户给定的脚本名称,其要跟随脚本名称变化):

script.sh {start|stop|restart|status}

(1) 调用时至少传递一个参数;否则,则显示帮助信息,并退出脚本;

(2) 如果参数为"start", 则创建空文件/var/lock/subsys/script.sh,并显示"starting script.sh successfully.";

(3) 如果参数为"stop",则删除空文件/var/lock/subsys/script.sh,并显示"stopping script.sh successfully.";

(4) 如果参数为"restart",则删除空文件/var/lock/subsys/script.sh,并显示"stopping script.sh successfully.";而后重新创建之,并显示"restarting script.sh successfully.";

(5) 如果参数为"status",则

(a) 如果文件/var/lock/subsys/script.sh文件存在,则显示"running";

(b) 否则,则显示为"stopped"

(6) 其它任意参数,均显示帮助信息后退出;帮助信息格式为命令使用格式;

#!/bin/bash## chkconfig: 2345 95 5# description: test service script#prog=`basename $0`lockfile=/var/lock/subsys/$progif [ $# -lt 1 ]; then    echo "Usage: $prog {start|stop|restart|status}"    exit 1fiif [ "$1" == 'start' ]; then    if [ -e $lockfile ]; thenecho "$prog is aleady running."exit 1    elsetouch $lockfileecho "Starting $prog succefully."    fielif [ "$1" == 'stop' ]; then    if [ -e $lockfile ]; thenrm -f $lockfileecho "Stopping $prog succefully."    elseecho "$prog is not running."exit 1    fielif [ "$1" == 'restart' ]; then    if [ -e $lockfile ]; thenrm -f $lockfileecho "Stopping $prog succefully."touch $lockfileecho "Starting $prog succefully."    elsetouch $lockfileecho "Starting $prog succefully."    fielif [ "$1" == 'status' ];then    if [ -e $lockfile ];thenecho "$prog is running."    elseecho "$prog is stopped."    fielse    echo "Usage: $prog {start|restart|status|stop}"    exit 1fi

条件测试语法:

单分支:

if CONDITION; thenCMD1CMD2...fi


例如:求100以内所有偶数之和;遍历100以内所有正整数;

#!/bin/bashdeclare i sum=0for i in {1..100} ; do        if [[ $[$i%2] -eq 0 ]] ; then                sum=$(($sum+$i))        fidoneecho $sum


例如:传递一个参数给脚本,而后以此参数为用户名,添加此用户;

#!/bin/bash

if [ $# -ge 1 ];then

useradd $1

fi


if可以嵌套:

if CONDITION1; thenif CONDITION2; thenCMDfifi


例如:传递一个参数给脚本,而后以此参数为用户名,不存在时,则添加此用户;

条件取反:

! CONDITION

#!/bin/bash            至少有个参数if [ $# -ge 1 ];thenif ! id $1 &>/dev/null;then           条件取反useradd $1fifi

if条件判断为真才能执行then下面的步骤

id $1 的状态如果是0,表示执行状态成功了,取反后条件是假,then就不执行

id $1 的执行状态如果是1,表示执行状态是失败的,取反后条件判断是真,then就开始执行


练习:写一个脚本

(1) 添加用户testuser1-tuser10;

(2) 用户不存在时才添加;

(3) 统计真正添加的用户数;

#!/bin/bash#declare -i newusers=0for i in {1..10}; do    if ! id testuser$i &> /dev/null; then        useradd testuser$i        let newusers++    fidoneecho "New users: $newusers."


练习: 写一个脚本

(1) 用ping命令测试172.16.100.X内的所有主机;

(2) 将在线的主机输出出来;

#!/bin/bash#for i in {1..254}; do    if ping -c1 -w1 172.16.100.$i &> /dev/null; then        echo "172.16.100.$i is up."    fidone


双分支结构:

if CONDITION-TRUE; then分支1else分支2fi


练习1: 写一个脚本

(1) 用ping命令测试172.16.100.X内的所有主机;

(2) 将所有主机的在线状态输出出来;

#!/bin/bashfor i in {1..254}doif ping -c1 -w1 172.16.100.$i &>/dev/null;thenecho "172.16.100.$i is up"elseecho "172.16.100.$i is down"fidone


练习:写一个脚本

(1) 传递两个整数参数给脚本;

(2) 返回其较大一个;

#!/bin/bashdeclare -i max=0for i in $* ; doif [ $i -gt $max ] ; thenmax=$ifidoneecho $max


练习:写一个脚本

(1) 传递两个以上的整数给脚本;

(2) 返回其较大者;

#!/bin/bash#declare -i max=0for i in $*; do    if [ $i -gt $max ]; then        max=$i    fidoneecho "max: $max."


练习: 写一个脚本

(1) 取得当前的主机名;

(2) 如果当前的主机名为localhost,则将其修改为oracle;否则,显示其名称;

#!/bin/bashhostname=`hostname`if [ "$hostname" == 'localhost' ]; then        hostname oracle        echo "oracle" > /proc/sys/kernel/hostnamefi

练习:写一个脚本

(1) 传递两个文本文件路径给脚本;

(2) 显示两个文件中空白行数较多的文件及其空白行数;

(3) 显示两个文件中总行数较多的文件及其行数;

#!/bin/bashif [ $# -ge 2 ] ; then        if [[ $(wc -l $1|cut -d" " -f1 ) -gt $(wc -l $2| cut -d" " -f1 ) ]] ; then                if [[ $(grep "^$" $1 | wc -l) -gt $(grep "^$" $2 | wc -l) ]] ; then                        echo "the more space line file is $1 "                        echo "the file space line is $(grep "^$" $1 | wc -l)"                else                        echo "the more space line file is $2 "                        echo "the file space line is $(grep "^$" $2 | wc -l)"                fi                echo "the more line file is $1 "                echo "the line have $(wc -l $1)"        else                echo "the more line file is $2 "                echo "the line have $(wc -l $2)"        fifi


练习:写一个脚本

(1) 传递一个参数给脚本,此参数为用户名;

(2) 如果用户存在,则执行如下任务

(a) 如果用户的id号小于500,显示其为管理员或系统用户;

(b) 否则,显示其为普通用户;

(3) 如果用户不存在,则添加之;

#!/bin/bash#if id $1 &> /dev/null; then     userid=`id -u $1`    if [ $userid -lt 500 ]; then        echo "$1 is sysadmin or sysuser."    else        echo "A common user."    fielse    useradd $1    if [ $? -eq 0 ];then        echo "Add user $1."    else        echo "Cannot add $1."    fifi



多分支的if语句:

if CONDITION1-TRUE; then分支1elif CONDITION2-TRUE; then分支2elif CONDITION3-TRUE; then分支3...else分支nfi


练习:传递一个参数给脚本

如果参数为quit,则显示说要退出脚本;

如果参数为yes,则显示说继续;

否则,则显示为无法识别;

#!/bin/bashif [[ "$1" == 'quit' ]] ; thenecho "quit"elif [[ "$1" == 'yes' ]] ; thenecho "yes"else echo "unkown"fi


练习:传递一个用户名参数给脚本

(1) 如果用户的id号为0,则显示为管理员;

(2) 如果用户的id号大于6000,则显示为guest;

(3) 如果用户的id号大于500,则显示为普通用户;

(4) 如果用户的id号大于0, 则显示为系统用户;

(5) 否则,无法识别;

#!/bin/bashif id $1 &/dev/null ; thenuserid=`id -u $1`if [ $userid -eq 0 ] ; thenecho "$1 is sysadmin"elif [ $userid -gt 60000 ] ; thenecho "$1 is a guest user"elif [ $userid -gt 500 ] ; thenecho "$1 is a common user"elif [ $userid -gt 0 ] ; thenecho "$1 is a system user"elseecho "$1 is unknown "fi



练习3:写一个脚本;

(1) 传递一个磁盘设备文件给脚本;

(2) 判断此设备是否存在;如果存在,则清除此设备上的所有分区;

(3) 否则,则无此设备;

#!/bin/bashif fdisk -l $1  ; thendd if=/dev/zero of=$1 bs=512 count=1elseecho "no such device"fi





组合测试条件1:

给条件添加逻辑操作符;

或, -o: [ -z "$hostname" -o "$hostname" == 'localhost' ]

与, -a: [ $uid -gt 0 -a $uid -lt 500 ]

非:[ ! EXPRESSION ]


练习:写一个脚本,取得当前的主机名,判断

(1) 如果主机名为空或为"localhost",则将其命名为stuX.oracle.com;

(2) 否则,则显示主机名即可;

#!/bin/bash#hostname=`hostname`if [ "$hostname" == 'localhost' -o -z "$hostname" ]; then    hostname stu100.oracle.com    #echo "stu100.oracle.com" > /proc/sys/kernel/hostnameelse    echo "The hostname is: $hostname."fi


练习:写一个脚本,传递一个参数给脚本;此参数为用户名

(1) 如果用户不存在,则直接退出脚本;

(2) 如果用户存在,

id=0,则显示为"system admin"

0

id>=500,则显示为"Common user."

#!/bin/bash#if ! id $1 &> /dev/null; then    echo "No such user."    exit 1fiuid=$(id -u $1)if [ $uid -eq 0 ]; then    echo "Sys Admin."elif [ $uid -gt 0 -a $uid -lt 500 ];then    echo "Sys User."else    echo "Common User."fi


练习:写一个脚本,传递一个参数给脚本;此参数为用户名

(1) 如果用户不存在,则直接退出脚本;

(2) 如果用户的id号大于等于500,且其shell为"以sh结尾的"类型,则显示"a user can log system.";否则,显示用户无法登录;

#!/bin/bash#if ! id $1 &> /dev/null; then    echo "No such user."    exit 1fiif [[ `id -u $1` -ge 500 ]] && [[ `grep "^$1\>" /etc/passwd | cut -d: -f7` =~ /bin/.*sh$ ]]; then    echo "a user can log system."else    echo "a user cannot log."fi


组合测试条件2:

短路操作符

与:COMMAND1 && COMMAND2

COMMAND1的退出状态如果为假,则COMMAND2不用运行,即可有最终结果;

或:COMMAND1 || COMMAND2

COMMAND1的退出状态如果为真,则COMMAND2不用运行,即可有最终结果;

非:! COMMAND


[ ! -d /tmp/test ] && mkdir /tmp/test^C[ -d /tmp/test ] || mkdir /tmp/test


练习:写一个脚本,完成如下任务:

(1) 如果httpd进程或nginx进程处于运行中,则显示"web server started.",并显示其监听的端口;

(2) 否则显示"no web server.";

if pidof httpd &> /dev/null && pidof nginx &> /dev/null;



交互式脚本:

read [OPTIONS] [name ...]

-p "PROMPT"

-t #: 超时时长


给变量以默认值:

[ -z "$VAR" ] && VAR=VALUE


练习:显示如下菜单给用户

cpu) show cpu infomation;

mem) show memory infomation;

disk) show disk infomation;

*) quit

提示用户键入选项:

(1) cpu: 显示CPU相关的信息

(2) mem: 显示内存相关的信息

(3) disk: 列出磁盘设备

(4) 其它任何信息,即为退出脚本


#!/bin/bash#cat << EOFcpu) show cpu infomation;mem) show memory infomation;disk) show disk infomation;*) quit=========================================================EOFread -p "Your  choice: " choiceif [[ "$choice" == 'cpu' ]]; then    lscpuelif [[ "$choice" == 'mem' ]]; then    free -melif [[ "$choice" == 'disk' ]]; then    fdisk -l /dev/sd[a-z]else    echo "quit"    exit 0fi

case语句:

简洁版的多分支if语句;


语法格式:

case 变量引用  in PATTERN1)分支1;;PATTERN2)分支2;;...*)分支n;;esac


PATTERN可使用通配符:

*:任意长度的任意字符

?: 任意单个字符

[]:指定范围内的任意单个字符

a|b: a或者b


练习:写一个脚本,使用tar工具把/etc目录备份至/backup目录中,名字为/backup/etc-日期时间.tar.{xz|bz2|gz};

(1) 显示如下菜单

xz) xz compress tool

gzip) gzip compress tool

bzip2) bzip2 compress tool

*) wrong choice and quit

(2) 根据用户选择的工具,执行相应操作;如果用户没有键入任何数据,则默认使用xz;


#!/bin/bashcat<

练习:使用case语句编写一个服务脚本框架

#!/bin/bash## chkconfig:# description: #prog=`basename $0`lockfile=/var/lock/subsys/$progcase $1 in "start")if [ -e $lockfile ];thenecho "$prog is aleady running"exit 1elsetouch $lockfileecho "Starting $prog succefully"fi;;"stop")if [ -e $lockfile ];thenrm -f $lockfileecho "Stopping $prog succefully"elseecho "$porg is not running"exit 1fi;;"restart")if [ -e $lockfile ];thenrm -f $lockfileecho "Stopping $prog succefully"touch $lockfileecho "Starting $prog succefully"elsetouch $lockfileecho "Starting $prog succefully"        fi;;"status")if [ -e $lockfile ];thenecho "$prog is running"elseecho "$prog is stopped"fi;;*)echo "Usage: $prog {start|restart|status|stop}"exit 1;;esac




bash中生成伪随机数:$RANDOM

0-32767


练习:生成10个随机数,返回其最大值;


#!/bin/bashdeclare -i max=0for i in {1..10};dorandom=$RANDOMLIST="$LIST $random"if [ $random -ge $max ];thenmax=$randomfidoneecho "List Number:$LIST"echo "Max Number:$max"



练习:从所有同学中随机挑选一个学号;

#!/bin/bashi=$[$RANDOM%36+1]j=$[$RANDOM%36+1]while [ $i -ne $j ] ; do        echo $i        echo $j        breakdone# echo $[`date +%N`%36+1]# echo $[`head /dev/urandom | cksum |cut -d" " -f1`%36+1]




while、until循环和函数:


练习:使用for循环,打印九九乘法表

#!/bin/bash#for i in {1..9}; do    for j in `seq 1 $i`; do        echo -n -e "${j}x${i}=$[$i*$j]\t"    done    echodone


while循环:

while CONDITION; do

循环体

循环控制变量的修正表达式

done


当CONDITION为"真"进入循环,直到"假"退出循环;


练习:计算100以内所有偶数之和;

#!/bin/bash#declare -i i=1declare -i evensum=0while [ $i -le 100 ]; do   if [ $[$i%2] -eq 0 ]; thenlet evensum+=$i   fi   let i++doneecho $evensum


until循环:

until CONDITION; do

循环体

循环控制变量修正表达式

done


当CONDITION为"假"时进入循环;为"真"退出循环;


练习:计算100以内所有偶数之和;

#!/bin/bash#declare i=1declare evensum=0until [ $i -gt 100 ]; do    if [ $[$i%2] -eq 0 ]; thenlet evensum+=$i    fi    let i++doneecho $evensum


练习:分别使用while和until循环实现添加10个用户:myuser1-myuser10

#!/bin/bash#declare -i i=1declare -i j=0until [ $i -gt 10 ]; do    if ! id myuser$i &> /dev/null; thenuseradd myuser$i    let j++    fi    let i++doneecho "Add users [total]: $j."


练习:打印九九乘法表,要求内外层循环分别使用while或until;

#!/bin/bashdeclare i=1declare j=1while [ $i -le 9 ] ; dowhile [ $j -le 9 ] ; doif [ $j -gt $i ] ; thenbreakfiecho -e -n "${j}x${i}=$[$i*$j]\t"let j++doneecholet j=1let i++done



循环控制命令:

break:提前退出循环;

break [N]: 退出N层循环;N省略时表示退出break语句所在的循环;

continue: 提前结束本轮循环,而直接进入下轮循环;

continue [N]:提前第N层的循环的本轮循环,而直接进入下轮循环;


while CONDITION; do

CMD1

if CONDITION2; then

break [N]

fi

CMD2

...

done


死循环:

while true; do

循环体

done


until false; do

循环体

done


练习:写一个脚本,判断给定的用户是否登录了当前系统;

(1) 如果登录了,则脚本终止;

(2) 每5秒种,查看一次用户是否登录;


方法一:使用until循环

#!/bin/bash#declare -i status=0who | grep "centos" &> /dev/nullstatus=$?until [ $status -eq 0 ]; do    sleep 5    who | grep "centos" &> /dev/null    status=$?doneecho "centos is logged on."



方法二:简化版

#!/bin/bash#declare -i status=0until who | grep "centos" &> /dev/null; do    sleep 5doneecho "centos is logged on."


方法三:使用死循环

#!/bin/bash#while true; do    who | grep "centos" &> /dev/null    if [ $? -eq 0 ];thenbreak    fi    sleep 5doneecho "centos is logged."


while的特殊用法:遍历指定文件的每一行

while read line; do

循环体

done < /path/from/somefile


练习:找出其UID为偶数的所有用户的用户名;并显示其ID号;


#!/bin/bash#file=/etc/passwdwhile read line; do    userid=`echo $line | cut -d: -f3`    if [ $[$userid%2] -eq 0 ]; thenecho $line | cut -d: -f1,3    fidone < $file


练习:输出给定的文件的偶数行的行号,以及其行内信息统统改为大写字母输出;

#!/bin/bashdeclare -i i=1while read line; do    if [ $[$i%2] -eq 0 ]; then    echo -n "$i "    echo $line | tr 'a-z' 'A-Z'    fi    let i++done < /path/from/somefile


练习:显示如下菜单给用户

cpu) cpuifno

mem) memory infomation

disk) disk infomation

quit) quit


(1) 用户给定的选项后,显示相应的信息;而后提示用户再次选择;

(2) 非正确选择也提示用户重新选择,并说明,如果想退出请输入"quit";


#!/bin/bash#while true; docat << EOFcpu) cpumem) memorydisk) diskquit) quitEOF    read -p "Your choice: " choice    case $choice in    cpu)        lscpu ;;    mem)        free ;;    disk)        fdisk -l /dev/sd[a-z] ;;    quit)        break ;;    esacdone



函数:function

过程式编程:代码重用

模块化编程;

简洁


语法:

function f_name {

函数体

}


f_name() {

函数体

}


调用:使用函数名

函数名出现的地方,会被自动替换为函数代码;


练习:利用函数改写此前的服务脚本:

#!/bin/bash#prog=`basename $0`lockfile=/var/lock/subsys/$progstart() {    if [ -e $lockfile ]; then        echo "$prog is already running."    else        touch $lockfile        [ $? -eq 0 ] && echo "Starting $prog finished."    fi}stop() {    if [ -e $lockfile ]; then        rm -f $lockfile        [ $? -eq 0 ] && echo "Stoping $prog finished."    else        echo "$prog is stopped yet."    fi}case $1 in"start")    start ;;"stop")    stop ;;"restart")    stop    start ;;*)    echo "Usage: $prog {start|stop|restart}"    exit 1esac

函数的返回值

函数的执行结果返回值:

函数中使用打印语句:echo, printf

函数体中OS命令执行结果的输出

函数的退出状态码

默认取决于函数体执行的最后一个命令的退出状态码;

自定义退出状态码:

return [0-255]

注意:函数体运行时,一旦遇到return语句,函数即返回;


函数可接受参数

传递参数给函数:调用函数时,在函数名后给出参数列表即可;例如"testfunc arg1 arg2 arg3"


在函数体中可使用$1, $2,...来调用传递过来的各参数;

可使用类似脚本的特殊变量:

$*, $@: 一次性获取参数列表:

$#: 参数的个数


判定用户输入用户名是否正确与否分别执行不同操作

#!/bin/bash#showuserinfo() {    [ $# -lt 1 ] && return 1    ! id $1 &> /dev/null && return 2    grep "^$1\>" /etc/passwd | cut -d: -f7    [ $? -eq 0 ] && return 0 || return 3}while true; do    read -p "Enter a username: " username    [ "$username" == 'quit' ] && break    showuserinfo $username    [ $? -ne 0 ] && echo "There is something wrong."done



练习:利用函数实现打印九九乘法表;

#!/bin/bashfunc() {declare i=1declare j=1while [ $i -le 9 ] ; do        while [ $j -le 9 ] ; do                if [ $j -gt $i ] ; then                        break                fi                echo -e -n "{$j}x$i=$[$i*$j]\t"                let j++        done                echo                let j=1                let i++        done}func



练习:写一个脚本,完成如下功能(使用函数):

1、提示用户输入一个可执行命令;

2、获取这个命令所依赖的所有库文件(使用ldd命令);

3、复制命令至/mnt/sysroot/对应的目录中

解释:假设,如果复制的是cat命令,其可执行程序的路径是/bin/cat,那么就要将/bin/cat复制到/mnt/sysroot/bin/目录中,如果复制的是useradd命令,而useradd的可执行文件路径为/usr/sbin/useradd,那么就要将其复制到/mnt/sysroot/usr/sbin/目录中;

4、复制各库文件至/mnt/sysroot/对应的目录中;

#!/bin/bash#target=/mnt/sysroot[ -d $target ] || mkdir $targetpreCmd() {    if which $1 &> /dev/null; thencmdpath=$(which --skip-alias $1)return 0    elseecho "No such command."return 1    fi}cmdCopy() {    cmddir=$(dirname $cmdpath)    [ -d $target/$cmddir ] || mkdir -p $target/$cmddir    [ -f $target/$cmdpath ] || cp $1 $target/$cmddir    return 0}libCopy() {    for lib in $(ldd $1 | grep -E -o "/[^[:space:]]+"); dolibdir=$(dirname $lib)[ -d $target/$libdir ] || mkdir -p $target/$libdir[ -f $target/$lib ] || cp $lib $target/$libdir    done    return 0}main() {    while true; do        read -p "Plz enter a command(quit for quiting): " cmd        [ "$cmd" == 'quit' ] && break        preCmd $cmd        if [ $? -eq 0 ]; then   cmdCopy $cmdpath            libCopy $cmdpath        fi    done}main



变量的作用域

本地变量:整个脚本,在脚本的函数中也可调用,也可修改;

局部变量:函数调用的整个生命周期;

local VAR=VALUE


函数递归

函数直接或间接调用函数自身

1 1 2 3 5 8 13

阶乘:10!

n(n-1)!

n(n-1)(n-2)!

阶乘函数

fact() {

if [ $1 -eq 0 -o $1 -eq 1 ]; then

echo 1

else

echo $[$1*$(fact $[$1-1])]

fi

}

下一篇接:Bash Shell脚本编程笔记总结(二)

0