shell编程中常用的语句if,case语句,expect用法,脚本语句控制器break,continue的区别

一,if语句

1,格式一:比较常用的格式(elif-then数量可以加)

if
then
elif
then
else
fi

格式二:也会用到

if
then
fi

2,shell编程

示例:判断文件是否存在,如果存在判断类型

####    “common file”是一个整体,如果不加引号,那么会误以为这是两个参数!!!

####[ ! -e "$1" ]是不等于,-e前面加上!和空格

####   -eq是判断纯数字是否相等
####   =是判断字符是否相等

注意:1,if语句里面调用了函数,这个函数执行完了要写上exit退出脚本,防止继续向下执行

2,要保证if语句的完整性,函数里面有if必须要写上fi,否则会报错

check_file.sh: line 9: syntax error near unexpected token `}'
check_file.sh: line 9: `}'

测试

[root@localhost mnt]# sh check_file.sh 
Please input a file follow script!!!
[root@localhost mnt]# sh check_file.sh /mnt/
/mnt/ is directory
[root@localhost mnt]# sh check_file.sh /etc/passwd
/etc/passwd is common file
[root@localhost mnt]# sh check_file.sh /etc/system-release
/etc/system-release is link
[root@localhost mnt]# sh check_file.sh /dev/vdb1
/dev/vdb1 is not exist !!!   
[root@localhost mnt]# sh check_file.sh /dev/vda1 
/dev/vda1 is block
成功!!!


示例:用给定的userfile和passwdfile创建用户,如果文件数量不对报错,如果文件不存在报错,文件行数差异报错,用户存在显示用户存在,但是不改变此用户密码,当用户不存在时建立用户并设定相应密码


#!/bin/bash
Check_USER()                 检查用户是否存在,创建用户的函数
{
        USERFILE_LINE=`awk 'BEGIN{N=0}{N++}END{print N}' $1`   查看有多少个用户
        for i in `seq 1 $USERFILE_LINE`
        do
        USERi=$(sed -n "${i}p" $1)   第i个用户
        PASSWDi=$(sed -n "${i}p" $2)  对应第i个密码
        getent passwd $USERi &> /dev/null && {      getent passwd是查看/etc/passwd有没有第i个用户的信息并把结果放在垃圾箱
                 echo "$USERi is exist!!!"  如果存在,打印用户存在
        } || {                               如果不存在,建立用户
                useradd $USERi
                echo $PASSWDi | passwd --stdin $USERi
                }
        done
}

if
        [ "$#" -ne "2" ]         判断文件数量是否正确
then
        echo "Please input userfile and passwdfile !!!"
        exit 0
elif
        [ ! -e "$1" ]           判断第一个文件是否存在
then
        echo $1 is not exist
        exit 0
elif
        [ ! -e "$2" ]            判断第二个文件是否存在
then
        echo $2 is not exist
        exit 0
elif
        USERFILE_LINE=`awk 'BEGIN{N=0}{N++}END{print N}' $1`    ####虽然这里是条件的判断,但是这里还是可以做一些简单的变量定义
        PASSWDFILE_LINE=`awk 'BEGIN{N=0}{N++}END{print N}' $2`
        [ "$USERFILE_LINE" -ne "$PASSWDFILE_LINE" ]           awk打印行数,判断两个文件的行数是否相等
then
        echo "Error:they are different in lines"
        exit 0
else
        Check_USER $1 $2     直接调用函数
fi

测试

[root@localhost scripts]# cat userfile 
user1
user2
user3
[root@localhost scripts]# cat passwdfile 
user11
user22
user33
[root@localhost scripts]# sh user_create.sh    测试脚本后面所跟数量
Please input userfile and passwdfile !!!
[root@localhost scripts]# sh user_create.sh userfile
Please input userfile and passwdfile !!!
[root@localhost scripts]# sh user_create.sh userfile pass  测试文件是否存在
pass is not exist
[root@localhost scripts]# sh user_create.sh user
Please input userfile and passwdfile !!!
[root@localhost scripts]# sh user_create.sh userfile passwdfile  建立用户
Changing password for user user1.
passwd: all authentication tokens updated successfully.
Changing password for user user2.
passwd: all authentication tokens updated successfully.
Changing password for user user3.
passwd: all authentication tokens updated successfully.
[root@localhost scripts]# id user1      查看用户
uid=1001(user1) gid=1001(user1) groups=1001(user1)
[root@localhost scripts]# id user2
uid=1002(user2) gid=1002(user2) groups=1002(user2)
[root@localhost scripts]# id user3
uid=1003(user3) gid=1003(user3) groups=1003(user3)
[root@localhost scripts]# vim userfile 
[root@localhost scripts]# cat userfile 
user2
user3
[root@localhost scripts]# userdel -r user1
[root@localhost scripts]# userdel -r user2
[root@localhost scripts]# userdel -r user3
[root@localhost scripts]# sh user_create.sh userfile passwdfile   测试文件的行数不一样
Error:they are different in lines
[root@localhost scripts]# vim userfile 
[root@localhost scripts]# sh user_create.sh userfile passwdfile 
Changing password for user user1.
passwd: all authentication tokens updated successfully.
Changing password for user user2.
passwd: all authentication tokens updated successfully.
Changing password for user user3.
passwd: all authentication tokens updated successfully.
[root@localhost scripts]# sh user_create.sh userfile passwdfile   测试用户存在时打印用户存在
user1 is exist!!!
user2 is exist!!!
user3 is exist!!!

成功!!!


示例:脚本后面跟cat,输出dog,脚本后面跟dog,输出cat,其他报错

测试:

[root@localhost mnt]# sh dogcat.sh
error:please input cat or dog
[root@localhost mnt]# sh dogcat.sh haha
error:please input cat or dog
[root@localhost mnt]# sh dogcat.sh dog
cat
[root@localhost mnt]# sh dogcat.sh cat
dog
[root@localhost mnt]# sh -x dogcat.sh       这里要判断两次,虽然成功了,但是效率很低
+ '[' '' = dog ']'
+ '[' '' = cat ']'
+ echo error:please input cat or dog
error:please input cat or dog
[root@localhost mnt]# sh -x dogcat.sh cat   这里要判断两次,虽然成功了,但是效率很低
+ '[' cat = dog ']'
+ '[' cat = cat ']'
+ echo dog
dog
[root@localhost mnt]# sh -x dogcat.sh dog   这里只判断了一次
+ '[' dog = dog ']'
+ echo cat
cat
成功!!!


二,case语句

1,格式

case
word1 )
action1
;;
word2)
action2
;;
........
*)
action_last
esac

2,shell编程

示例:脚本后面跟cat,输出dog,脚本后面跟dog,输出cat,其他报错

不用if语句,用case语句

*是匹配所有,也就是除了dog,cat之外的其他情况

测试:

[root@localhost mnt]# sh catdog.sh 
error:please input cat or dog
[root@localhost mnt]# sh catdog.sh haha
error:please input cat or dog
[root@localhost mnt]# sh catdog.sh cat
dog
[root@localhost mnt]# sh catdog.sh dog
cat
[root@localhost mnt]# sh -x catdog.sh       在if语句中要判断两次,case只需要匹配一次,提高了效率
+ case $1 in
+ echo 'error:please input cat or dog'
error:please input cat or dog
[root@localhost mnt]# sh -x catdog.sh haha  在if语言中要判断两次,case只需要匹配一次,提高了效率
+ case $1 in
+ echo 'error:please input cat or dog'
error:please input cat or dog
[root@localhost mnt]# sh -x catdog.sh cat
+ case $1 in
+ echo dog
dog
[root@localhost mnt]# sh -x catdog.sh dog
+ case $1 in
+ echo cat
cat
成功!!!


注意:case和if语句都可以用来做条件判断,但是一般来说,case用来做简单的字符,数字匹配判断,而if语言通常做条件判断,强调条件!!!


三,EOF标准输入重定向

1,格式

commend <<EOF
answer
answer
EOF    ####注意:这里不一定必须是EOF,其他的也可以,但是开始和结尾必须一样,特别注意有时候前面会有tab键导致格式不一样,颜色一样就没问题了。

2,shell编程

示例:已知磁盘有第三主分区,建立自动应答脚本删除磁盘的第三个主分区,并同步分区表

这个应答过程是按照执行fdisk /dev/vdb之后的过程写的,d 3 wq 都是在执行这条命令之后,我们和系统的交互应答,partprobe是在执行fdisk /dev/vdb交互应答之后的才执行的,所以要放在EOF外面

测试:

[root@localhost mnt]# fdisk -l /dev/vdb 

Disk /dev/vdb: 10.7 GB, 10737418240 bytes, 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x2b9adf21

   Device Boot      Start         End      Blocks   Id  System
/dev/vdb1            2048      206847      102400   83  Linux
/dev/vdb2          206848      411647      102400   83  Linux
/dev/vdb3          411648      616447      102400   83  Linux        ####原来有第三个分区
[root@localhost mnt]# sh del_disk.sh                                 ####执行脚本
Welcome to fdisk (util-linux 2.23.2).

Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): Partition number (1-3, default 3): Partition 3 is deleted

Command (m for help): The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.
Warning: Unable to open /dev/sr0 read-write (Read-only file system).  /dev/sr0 has been opened read-only.
Warning: Unable to open /dev/sr0 read-write (Read-only file system).  /dev/sr0 has been opened read-only.
Warning: Unable to open /dev/sr0 read-write (Read-only file system).  /dev/sr0 has been opened read-only.
[root@localhost mnt]# fdisk -l /dev/vdb 

Disk /dev/vdb: 10.7 GB, 10737418240 bytes, 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x2b9adf21

   Device Boot      Start         End      Blocks   Id  System
/dev/vdb1            2048      206847      102400   83  Linux
/dev/vdb2          206848      411647      102400   83  Linux        ####第三个分区被删除
成功!!!


示例:如果不存在扩展分区,建立扩展分区

注意:1,这里用到了if-then-fi语句,并不一定要求出现elif

2,<<EOF里面有三个空行,这相当于敲了三个回车

3,DESK_MESSAGE是awk查看有没有扩展分区

测试

[root@localhost mnt]# fdisk /dev/vdb 

   Device Boot      Start         End      Blocks   Id  System
/dev/vdb1            2048      206847      102400   83  Linux
/dev/vdb2          206848      411647      102400   83  Linux        ###没有扩展分区

[root@localhost mnt]# sh create_disk.sh                              ###执行脚本
Welcome to fdisk (util-linux 2.23.2).

Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): Partition type:
   p   primary (2 primary, 0 extended, 2 free)
   e   extended
Select (default p): Partition number (3,4, default 3): First sector (411648-20971519, default 411648): Using default value 411648
Last sector, +sectors or +size{K,M,G} (411648-20971519, default 20971519): Using default value 20971519
Partition 3 of type Extended and of size 9.8 GiB is set

Command (m for help): The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.
Warning: Unable to open /dev/sr0 read-write (Read-only file system).  /dev/sr0 has been opened read-only.
Warning: Unable to open /dev/sr0 read-write (Read-only file system).  /dev/sr0 has been opened read-only.
Warning: Unable to open /dev/sr0 read-write (Read-only file system).  /dev/sr0 has been opened read-only.

[root@localhost mnt]# fdisk /dev/vdb                         ####出现扩展分区

   Device Boot      Start         End      Blocks   Id  System
/dev/vdb1            2048      206847      102400   83  Linux
/dev/vdb2          206848      411647      102400   83  Linux
/dev/vdb3          411648    20971519    10279936    5  Extended
[root@localhost mnt]# sh create_disk.sh           ####再次执行,扩展分区已经存在,没有执行结果
[root@localhost mnt]# sh  -x create_disk.sh  
++ fdisk -l
++ awk /Extended/
+ DESK_MESSAGE='/dev/vdb3          411648    20971519    10279936    5  Extended'
+ '[' -z '/dev/vdb3          411648    20971519    10279936    5  Extended' ']'
成功!!!

四,expect的用法

1,概述

expect 是自动应答命令,用于交互式命令的自动执行
spawn 是 expect 中的监控程序,运行后会监控命令提出的交互问题
send发送问题答案给交互命令
"\r"表示回车
exp_continue 表示当问题不存在时继续回答下面的问题
expect eof 表示问题回答完毕退出 expect 环境
interact  表示问题回答完毕留在交互界面 这个一般用在ssh连接里面,自动连接上了可以保持ssh的环境
set NAME [ lindex $argv n ] 定义变量 n从0开始,set NAME [ lindex $argv 0 ] 和脚本里面$1类似

注意:1,使用expect时要下载工具yum install expect.x86_64

2,expect不是脚本,不能使用脚本的那些命令

3,使用expect的文件后缀为.exp,幻数为#!/usr/bin/expect ,用expect而不是sh调用

4,如果.exp文件里面调用了.sh脚本,注意给脚本可执行权限

5,expect {   } 里面的关键字只要不重复九行,但是顺序不能变,因为每一个关键字对应一个答案,每一个答案对应一个问题。


2,shell编程

示例;建立ask.sh问题脚本,answer.exp自动应答,答案已知


[root@localhost mnt]# sh ask.sh 
What is your name: minz
How old are you: 6
Which obj do you study: linux
Are you happy ? : happy
minz is 6's years old  and study linux feel happy

#!/usr/bin/expect                     注意幻数
#####这一行也可以设定等待时间如果执行错误,等待多长时间set timeout 2 等待2秒
spawn /mnt/ask.sh                     spawn是监控程序,监控/mnt/ask.sh执行之后的交互问题
expect {                              注意变量的定义方法
        name { send "minz\r";exp_continue }       name是关键字,就是第一个问题what is your name的一个关键字
换成其他的也行,只要不和下面的重复,minz就是这个问题的答案,\r就是敲回车,由于下面还有问题需要回答,所以执行exp_continue
        old { send "6\r";exp_continue }
        study { send "linux\r";exp_continue }
        happy { send "happy\r" }          happy是Are you happy? 问题的关键字,happy是问题的答案,由于问题到此结束,所以不用写exp_continue
}
expect eof     表示回答完毕问题,退出expect环境

测试:

[root@localhost mnt]# chmod +x /mnt/ask.sh   ###调用了脚本,所以要加上可执行权限
[root@localhost mnt]# expect answer.exp      ###注意是expect调用.exp文件
spawn /mnt/ask.sh
What is your name: minz
How old are you: 6
Which obj do you study: linux
Are you happy ? : happy
minz is 6's years old  and study linux feel happy ###自动应答生效
成功!!!


示例:示例;建立ask.sh问题脚本,answer.exp自动应答,答案跟在.exp后面

注意:1,调用的脚本要加上可执行权限!!!

2,set NAME [ lindex $argv 0]  表示把.exp之后的第一个参数给NAME这个值,set AGE [ lindex $argv 1]   表示把.exp之后的第二个参数给AGE这个值

3,name { send "$NAME\r";exp_continue }  这里$NAME是调用NAME这个参数的值,与脚本里面用法类似,表示把NAME这个值作为答案给关键字是name的这个问题

测试:

[root@localhost mnt]# expect answer.exp   不加任何参数
spawn /mnt/ask.sh
What is your name: 
How old are you: 
Which obj do you study: 
Are you happy ? : 
 is 's years old  and study  feel 
[root@localhost mnt]# expect answer.exp haha 1 html sad   带上参数
spawn /mnt/ask.sh
What is your name: haha
How old are you: 1
Which obj do you study: html
Are you happy ? : sad
haha is 1's years old  and study html feel sad

示例:在上一个问题的基础上,把.exp文件改为.sh文件,并且可以执行(用EOF导入)

修改幻数 > EOF标准输入重定向 >  修改后缀 > 删除expect变量 > 修改变量

测试:

[root@localhost mnt]# mv answer.exp answer.sh    修改后缀
[root@localhost mnt]# sh answer.sh   不带参数
spawn /mnt/ask.sh
What is your name: 
How old are you: 
Which obj do you study: 
Are you happy ? : 
 is 's years old  and study  feel 
[root@localhost mnt]# sh answer.sh tom 2 java bad  带参数
spawn /mnt/ask.sh
What is your name: 2
How old are you: tom
Which obj do you study: java
Are you happy ? : bad
2 is tom's years old  and study java feel bad
成功!!!


示例:写一个auto_connect.sh,要求脚本后面跟ip 和 密码,登陆该主机


注意:1,这两个关键字中“yes/no”是当我们第一次连接别的主机时,~/.ssh/known_hosts没有记录信息,所以会询问,这个一定要写上,我们第二次连接时,虽然不用输入yes,写上不影响第二次连接的使用

2,set timeout 30 是延长等待时间,如果不写这个,连接时间如果稍长,就会误以为失败。

3,init 3是为了进入文本编辑模式,因为这个脚本无论是expect eof 还是interect执行脚本之后,都无法在连接的主机上面执行命令,会卡住,Iinit 3只是为了证明登陆成功,也可以输入hostname,显示值。

测试:

[root@localhost scripts]# sh auto_connect.sh 172.25.254.156 redhat
spawn ssh [email protected]
[email protected]'s password: 
Last login: Sun Jun 24 21:26:36 2018 from www.westos.com
[root@156 ~]#     ###此时执行命令会卡住

拓展:

注意:1,这个文件以.exp结尾,不是脚本,但是如果是interact模式,那么登陆上可以执行命令,expect eof模式下则会卡住

2,注意以expect调用

测试

[root@localhost scripts]# expect auto_connect.exp 172.25.254.156 redhat
spawn ssh [email protected]
[email protected]'s password: 
Last login: Sun Jun 24 21:38:59 2018 from www.westos.com
[root@156 ~]# ls        ###可以执行命令
anaconda-ks.cfg  Downloads                   gropu  Pictures  rht-ks-post.log  Templates
Desktop          file                        group  PM        rht-ks-pre.log   Videos
Documents        foundation-config-post.log  Music  Public    software

示例:写一个脚本,导出指定主机的域名和ip(已知密码,主机ssh都可访问)


注意:1,做过滤时在<<EOF后面做也行,HOSTNAME=`AUTO_CONNECT $1`后面也行

2,在linux系统中,文字换行默认加\n  在windows中,文字换行默认加\n\r   在unix系统中,文字换行默认加\r

由于grep是unix中的命令,所以会加\r,用sh -x auto_connect.sh redha  可以看见每行后面都加了\r

测试:

[root@example100 scripts]# sh -x auto_connect.sh redhat
+ for i in '{155..156}'
++ AUTO_CONNECT redhat
++ grep -E 'authenticity|fingerprint|connecting|Permanently|password|spawn' -v
++ /usr/bin/expect
+ HOSTNAME=$'155.example.com\r'      #####这里被加上了\r
 172.25.254.155'ple.com
+ for i in '{155..156}'
++ AUTO_CONNECT redhat
++ /usr/bin/expect
++ grep -E 'authenticity|fingerprint|connecting|Permanently|password|spawn' -v
+ HOSTNAME=$'156.example.com\r'      #####这里被加上了\r
 172.25.254.156'ple.com
[root@example100 scripts]# cat /mnt/scripts/host
 172.25.254.155
 172.25.254.156

注意:这里虽然采集到了,看起来只有ip,没有域名,但是vim查看

需要进行后期全局替换处理,ctrl+v ctrl+m =^M

!!!直接在脚本中修改,写入全局替换


再次测试:

[root@example100 scripts]# > /mnt/scripts/host
[root@example100 scripts]# sh auto_connect.sh redhat
[root@example100 scripts]# cat /mnt/scripts/host
155.example.com 172.25.254.155
156.example.com 172.25.254.156
成功!!!



五,脚本语句控制器continue,break的区别

exit n  脚本退出,退出值为 n,其中0是正常结束退出,其他都是非正常结束退出
break  退出当前循环,循环体中在break之后的内容不会执行,但是在循环语句之后的语句会执行
continue  提前结束当前循环,进入下一个循环,循环体中continue之后的内容不会执行,并且循环语句之后的语句也不会执行

示例:打印0-10中除去4的数字

###注意:1,不等于的!的位置在最前面!!!

2,当NUM不等于4时,[ ]条件为真,开始执行循环体里面的内容do-done,即打印$NUM,然后break退出本次循环。

3,当NUM等于4时,[ ]条件为假,不做循环体里面的内容

4,如果把break换成continue,那么只会不停的输出1.因为当NUM=1时,条件为真,开始执行循环体里面的内容,即打印NUM的值1,然后continue提前结束循环,不再看continue之后的所有语句,直接进入下一个循环,NUM还是为1,所以会一直循环







猜你喜欢

转载自blog.csdn.net/ha_weii/article/details/80786838
今日推荐