千家信息网

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

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

脚本 用户 文件 循环 参数 命令 函数 变量 状态 条件 测试 字符 目录 分支 结果 主机 编程 之和 设备 信息 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 怎么制作简单数据库 常用搜索图书的数据库 sql数据库方面的资料 域名服务器怎么样 苏州阿里云服务器哪里有 全球网络安全问题趋势 科技互联网创业计划书内容 打印服务器支持针式打印机吗 计算机网络技术职业规划word 反诈骗网络安全手抄报 腾讯云服务器账号管理 网络技术是近代信息技术吗 2021年网络安全新闻 中软国际网络安全违规举报邮箱 建数据库的主要技术 小程序数据库读取数据为空 计算机三级网络技术如何考 江苏通信软件开发服务推广 深圳市中电网络技术怎么样 枪战服务器 我的世界服务器命令方块禁用物品 网络安全的威胁可以分为哪两类 网络安全策略配置方法 什么是数据库文件的命令中 数据库查询乱码 辽宁职业学院计算机网络技术 中国著名网络安全工程师 焦作思启网络技术有限公司 网络安全词汇英语作文 通信网络技术湖南省技能竞赛
0