Linux Shell脚本编程-语句控制

过程式编程语言
bash脚本编程面向过程的编程
  顺序执行:默认法则,按照顺序一条一条语句执行
  选择执行:分支,条件判断,符合条件的分支予以执行
  循环执行:将同一段代码反复执行有限次,所以循环必须有退出条件,否则将陷入死循环

注意:
  顺序执行不需要特定的控制,只需按照语句依次执行即可;选择执行,则需要特定的控制语句(如:if、case)来判断执行;循环执行则需要特定的循环控制控制(如:for、while等)

bash脚本编程之用户交互
  在bash脚本编程过程当中有时程序需要与用户之间进行交互(通过键盘输入数据),如提示用户输入一些参数等,此时,我们可以使用read命令来完成此功能
  read 通过提示信息输入一些信息存到变量中用于引用,达到与脚本交互的目的
  -p 指定提示信息
  -s 隐藏输入
  -n 指定输入的长度
  -t 用户长时间没有输入就退出(默认单位为秒)
 read -p "提示信息" varName

 注意:read从标准输入中读取值,给每个单词分配一个变量,所有剩余单词都被分配给最后一个变量
演示:
[root@centos7 ~]# read name
tao
[root@centos7 ~]# echo $name
tao

[root@centos7 ~]# read a b
tom jerry
[root@centos7 ~]# echo $a
tom
[root@centos7 ~]# echo $b
jerry

# 如果给定的参数少于变量个数,那么对应的参数为空的变量将为空
[root@centos7 ~]# read a b c
tom jerry
[root@centos7 ~]# echo $a
tom
[root@centos7 ~]# echo $b
jerry
[root@centos7 ~]# echo $c

# 如果给定的参数多于变量,那么多余的参数都将赋值给最后一个变量
[root@centos7 ~]# read a b c
tao tom jerry xiu
[root@centos7 ~]# echo $a
tao
[root@centos7 ~]# echo $b
tom
[root@centos7 ~]# echo $c
jerry xiu

[root@centos7 ~]# echo -n "Enter a username: ";read name
Enter a username: tao
[root@centos7 ~]# echo $name
tao

[root@centos7 ~]# read -p "Enter a username: " name
Enter a username: tom
[root@centos7 ~]# echo $name
tom

[root@centos7 ~]# read -p "Enter a username: " name
Enter a username: tom
[root@centos7 ~]# [ -z "$name" ] && name="tao" # 如果变量为空时赋值为tao
[root@centos7 ~]# echo $name
tao

示例脚本:
[root@centos7 bin]# cat useradd2.sh
#!/bin/bash
#

read -p "Enter a username: " name
[[ -z "$name" ]] && echo "a username is needed" && exit 2


if id $name &> /dev/null; then
  echo "$name exits."
else
  read -p "Enter password for $name,[password]: " password
  [[ -z "$password" ]] && password="password"
  useradd $name
  echo "$password" | passwd --stdin $name &> /dev/null
  echo "Add user $name finished"
fi

给变量设置默认值:
  在这种交互式情况下如果用户没输入信息,给变量一个默认值的方法
  ${varName:-value}
  如果变量varName值不空,返回varName的值;如果为空返回值value;但是varName的值没有被改变为value
  varName=${varName:-value}
  如果变量varName值不空,变量varName的值不变;如果为空,变量varName的值为value

bash中条件判断if语句
  单分支:
  if 条件; then
    分支1
  fi

 双分支:
  if 条件; then
    分支1
  else
    分支2
  fi

  多分支:
  if 条件; then
    分支1
  elif 条件2; then
    分支2
  elif 条件3; then
    分支3
    ...
  else
    分支n
  fi
    从上而下逐条件进行判断,第一次遇到为"真"条件时,执行其分支,而后结束整个if语句;即便多个条件可能同时满足,分支也只会执行其中一个,即首先测试为真的那个
  命令用作条件判断,就表示引用的是其状态结果(即执行成功与否),而非命令的输出返回结果,因此,不能使用命令替换符反引号``或$( )
示例:
1、通过参数传递一个用户名给脚本,此用户不存在时,则添加
[root@centos7 bin]# cat useradd1.sh
# 单分支语句
#/bin/bash
#

if [ $# -lt 1 ]; then
  echo "At lest one username"
  exit 2
fi

if ! grep "^$1\>" /etc/passwd &> /dev/null;then
  useradd $1
  echo $1 |passwd --stdin $1 &> /dev/null
  echo "Add user $1 finished"
fi

#双分支语句
[ $# -lt 1 ] && echo "At lest add username" && exit 1
if grep "^$1\>" /etc/passwd &> /dev/null;then
  echo "user $1 exits"
else
  useradd $1
  echo $1 |passwd --stdin $1 &> /dev/null
  echo "Add user $1 finished"
fi

# 方法二
[[ $# -lt 1 ]] && echo "At lest one username" && exit 2
if id $1 &> /dev/null; then
  echo "user $1 exits"
else
  useradd $1
  echo $1 |passwd --stdin $1 &> /dev/null
  echo "Add user $1 finished"
fi

2、写一个脚本/root/bin/filetype.sh,判断用户输入文件路径,显示其文件类型(普通,目录,链接,其它文件类型)
[root@centos7 bin]# cat filetype.sh
#!/bin/bash
#

if [ $# -lt 1 ]; then
  echo "At least one path"
  exit 1
fi

if ! [ -e $1 ]; then
  echo "No such file"
  exit 2
fi

if [ -f $1 ]; then
  echo "Common file"
elif [ -d $1 ]; then
  echo "Directory."
elif [ -b $1 ]; then
  echo "Block special file."
elif [ -c $1 ]; then
  echo "Character special file"
elif [ -s $1 ]; then
  echo "Socket file."
else
  echo "Unknow."
fi

3、根据提示让用户选择输入的信息:cpu,mem,disks,quit,正确就的选择就给出相应的信息,否则就提示重新选择正确的选项
[root@centos7 bin]# cat menu.sh
#!/bin/bash
#

cat << EOF
cpu) display cpu information
mem) diplay men information
disks) display disks information
quit) quit
======================================
EOF

read -p "Enter your option: " option

while [ "$option" != "cpu" -a "$option" != "mem" -a "$option" != "disks" -a "$option" != "quit" ]; do
  echo "cpu mem disks quit"
  read -p "Enter your option again: " option
done

if [ "$option" == "cpu" ];then
  lscpu
elif [ "$option" == "mem" ];then
  free -m
elif [ "$option" == "disks" ];then
  fdisk -l /dev/[hs]d[a-z]
else
  echo "quit"
  exit 0
fi

bash中条件判断case语句
  条件判断case语句(主要用于离散值的匹配)
  使用场景:case 语句在很多场景当中可以替换多分支的 if 语句,显示为更为简洁的格式,但其仅适用于:一个变量与多个取值之间作等值或者模式匹配时才可以用(即:拿一个变量来回跟多个可能的变量取值作比较才能使用或者说是,一个大的变量里面还有许多小的变量要选择才会用到),否则的话,只能用多分支的 if 语句
 语法:
  case $变量名 in
  "值1")
    如果是变量的值等于值1,则执行程序1
    ;;
  "值2")
    如果变量的值等于值2,则执行程序2
    ;;
  *)
    如果变量的值不是以上的值,则执行此程序
    ;;
  esac

  case 值 in
  模式1)
    分支1
    ;;
  模式2)
    分支2
    ;;

  *)
    默认分支
    ;;
  esac

  case支持glob风格的通配符:
  *  任意长度任意字符
  ?  任意单个字符
  []  指定范围内的任意单个字符[adc](也可以表示范围[1-10])
  a|b a或b

示例:写一个脚本,完成如下任务
A、显示一个如下菜单:
cpu) show cpu information;
mem) show memory information;
disk) show disk information;
quit) quit
B、提示用户选择选项
C、显示用户选择的内容
#!/bin/bash
#
cat << EOF
cpu) show cpu information;
mem) show memory information;
disk) show disk information;
quit) quit
============================
EOF

read -p "Enter a option: " option
while [ "$option" != 'cpu' -a "$option" != 'mem' -a "$option" != 'disk' -a "$option" != 'quit' ]; do
  read -p "Wrong option, Enter again: " option
done

case "$option" in
cpu)
  lscpu
  ;;
mem)
  cat /proc/meminfo
  ;;
disk)
  fdisk -l
  ;;
*)
  echo "Quit..."
  exit 0
  ;;
esac

2、写一个服务脚本,完成如下任务
A、支持接受start,stop,restart,status四个参数之一;
B、如果参数非此四值,则提示使用帮助后退出;
C、start则创建lockfile,并启动;stop则删除lockfile,显示停止;restart,则先删除此文件,再创建此文件,而后显示启动完成;status,如果lockfile存在,则显示running,不存在,则显示 stopped

[root@centos7 bin]# cat testservice.sh
#!/bin/bash
#
#chkconfig: - 50 50
#description: test service scipt
#

prong=$(basename $0)
lockfile=/var/lock/subsys/$prong

case $1 in
start)
  if [ -f $lockfile ];then
    echo "$prong is running yet."
  else
    touch $lockfile
    [ $? -eq 0 ] && echo "start $prong finished."
  fi
  ;;
stop)
  if [ -f $lockfile ];then
    rm -f $lockfile
    [ $? -eq 0 ] && echo "stop $prong finished."
  else
    echo "$prong is not running."
  fi
  ;;
restart)
  if [ -f $lockfile ];then
    rm -f $lockfile
    echo "stop $prong finished."
    touch $lockfile
    echo "start $prong finished."
  else
    touch $lockfile
    echo "start $prong finished."
  fi
  ;;
status)
  if [ -f $lockfile ];then
    echo "$prong is running"
  else
    echo "$prong is stopped"
  fi
  ;;
*)
  echo "Usage: $prong {start|stop|restart|status}"
  exit 1
esac

#================================================================================
# 复制到 /etc/init.d/ 目录下:
[root@centos7 bin]# cp testservice /etc/init.d/
[root@centos7 bin]# ls /etc/init.d/
functions netconsole network README testservice.sh

# 加入到 SysV 服务中去,使用 service 命令管控
[root@centos7 bin]# chkconfig --add testservice
[root@centos7 bin]# chkconfig --list testservice
注意:该输出结果只显示SysV服务,并不包含原生systemd服务。SysV配置数据可能被原生 systemd 配置覆盖。
  查看systemd服务请执行:systemctl list-unit-files
  查看特定target启用的服务请执行:systemctl list-dependencies [target]
testservice 0:关 1:关 2:关 3:关 4:关 5:关 6:关

[root@centos7 bin]# ll /etc/init.d/testservice
-rw-r--r-- 1 root root 798 3月 2 13:42 /etc/init.d/testservice

# 给脚本一个执行权限
[root@centos7 bin]# chmod +x /etc/init.d/testservice

# 服务脚本测试如下
[root@centos7 bin]# service testservice start
start testservice finished.
[root@centos7 bin]# service testservice start
testservice is running yet.
[root@centos7 bin]# service testservice restart
stop testservice finished.
start testservice finished.
[root@centos7 bin]# service testservice stop
stop testservice finished.
[root@centos7 bin]# service testservice restart
start testservice finished.
[root@centos7 bin]# service testservice res
Usage: testservice {start|stop|restart|status}
[root@centos7 bin]# ls /var/lock/subsys/
network testservice
[root@centos7 bin]# service testservice stop
stop testservice finished.

# 注意:通过服务控制的脚本都会在启动完成后在/var/lock/subsys/下创建一个锁文件
[root@centos7 bin]# service testservice stop
stop testservice finished.
[root@centos7 bin]# ls /var/lock/subsys/
network
[root@centos7 bin]# service testservice status
testservice is stopped
[root@centos7 bin]# service testservice start
start testservice finished.
[root@centos7 bin]# ls /var/lock/subsys/
network testservice

练习:
1、写一个脚本/root/bin/createuser.sh,实现如下功能:使用一个用户名做为参数,如果指定参数的用户存在,就显示其存在,否则添加之;显示添加的用户的id号等信息

2、写一个脚本/root/bin/yesorno.sh,提示用户输入yes或no,并判断用户输入的是yes还是no,或是其它信息

3、写一个脚本/root/bin/checkint.sh,判断用户输入的参数是否为正整数

bash中循环执行for语句
for循环:通过使用一个变量去遍历给定列表中的每个元素。在每次变量赋值时执行一次循环体,直至赋值完成所有元素退出循环
  语法1
  for var_Name in 列表; do
    循环体(正常执行的执行命令)
    语句1
    语句2
    语句3
    ...
  done
  语法2
  for ((变量初始值;循环控制条件;变量变化));do
    循环体(正常执行的执行命令)
    语句1
    语句2
    语句3
    ...
  done
例子:从1加到100
#!/bin/bash
s=0
for ((i=1;i<=100;i=i+1));do
  s=$(($s+$i))
done
echo “The sum is:$s”

  列表的生成方法:
  直接给出列表
  生成数字列表
    大括号展开生成列表
    {start..end} 例子1到100 {1..100}
    通过seq命令生成列表,用的时候需要通过反引号``命令引用
    seq start       stop 例子1到100 `seq 1 100`
    seq start    step stop 例子1到100的奇数 `seq 1 2 100`
    seq 起始数字 步长 结束数字  
  返回列表的命令:$(COMMAND),如:$(ls)
  使用glob:如:*.sh
  变量引用:$@、$*

示例:
1、求100以内所有正整数之和
#!/bin/bash
#

declare -i sum=0

for i in {1..100};do
  echo "\$sum is $sum, \$i is $i"
  sum=$[$sum+$i]
done

echo $sum

# 执行效果如下:
[root@centos7 bin]# bash sum.sh
$sum is 0, $i is 1
$sum is 1, $i is 2
$sum is 3, $i is 3
$sum is 6, $i is 4
$sum is 10, $i is 5
$sum is 15, $i is 6
$sum is 21, $i is 7
$sum is 28, $i is 8
$sum is 36, $i is 9
$sum is 45, $i is 10
...
5050

# 100 以内所有偶数之和
[root@centos7 bin]# sum=0;for i in `seq 0 2 100`;do sum=$[$sum+$i];done;echo $sum
2550

# 100 以内所有奇数之和
[root@centos7 bin]# sum=0;for i in `seq 1 2 100`;do sum=$[$sum+$i];done;echo $sum
2500

# 求当前系统上所有用户的id之和
[root@centos7 bin]# sum=0;for i in `cut -d: -f3 /etc/passwd`;do sum=$[$sum+$i];done ;echo $sum
80311

2、判断/var/log/目录下所有文件的文件类型
[root@centos7 bin]# cat filetype1.sh
#!/bin/bash
#
for filename in /var/log/*;do
  if [ -f $filename ]; then
    echo "$filename is Common file"
  elif [ -d $filename ]; then
    echo "$filename is Directory."
  elif [ -b $filename ]; then
    echo "$filename is Block special file."
  elif [ -c $filename ]; then
    echo "$filename is Character special file"
  elif [ -s $filename ]; then
    echo "$filename is Socket file."
  else
    echo "$filename is Unknow."
  fi
done

  for循环的特殊格式
  for ((控制变量初始化;条件判断表达式;控制变量的修正表达式)); do
    循环体
  done

 控制变量初始化:仅在运行到循环代码段时执行一次
 控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后再做条件判断;

示例1:求100以内所正整数之和;
#!/bin/bash
#
declare -i sum=0
for ((i=1;i<=100;i++)); do
  let sum+=$i
done
echo "Sum: $sum."

示例2:打印九九乘法表;
#!/bin/bash
#
for((j=1;j<=9;j++));do
  for((i=1;i<=j;i++))do
    echo -e -n "${i}X${j}=$[$i*$j]\t"
  done
  echo
done

示例3:写一个脚本,完成如下任务
1、显示一个如下菜单:
cpu) show cpu information;
mem) show memory information;
disk) show disk information;
quit) quit
2、提示用户选择选项;
3、显示用户选择的内容;
#!/bin/bash
#
cat << EOF
cpu) show cpu information;
mem) show memory information;
disk) show disk information;
quit) quit
============================
EOF

read -p "Enter a option: " option
while [ "$option" != 'cpu' -a "$option" != 'mem' -a "$option" != 'disk' -a "$option" != 'quit' ]; do
  read -p "Wrong option, Enter again: " option
done

if [ "$option" == 'cpu' ]; then
  lscpu
elif [ "$option" == 'mem' ]; then
  cat /proc/meminfo
elif [ "$option" == 'disk' ]; then
  fdisk -l
else
  echo "Quit"
  exit 0
fi
进一步地:用户选择,并显示完成后不退出脚本;而是提示用户继续输入选择;直到输入quit才会退出;

bash中循环执行while语句
  while循环是不定循环,也称作条件循环。只要条件判断式成立,循环就会一直继续,直到条件判断式不成立,循环才会停止,这就和for的固定循环不太一样了;适用于循环次数未知的场景,要有退出条件;如何让while循环退出,在循环体中改变测试条件中用于控制循环变量的值;

while语法:
 while CONDITION; do
   循环体
 done

 循环进入条件:CONDITION为true
 循环退出条件:CONDITION为false

示例:求100以内所有正整数之和;
#!/bin/bash
declare -i sum=0
declare -i i=1
while [ $i -le 100 ]; do
  let sum+=$i
  let i++
done
echo "$i"
echo "Summary: $sum."

while循环的特殊用法(遍历文件的每一行)
语法:
  while read VARIABLE; do
   循环体
 done < /PATH/FROM/SOMEFILE
解释:依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量VARIABLE

示例1s:找出ID号为偶数的用户,显示其用户名,ID,及默认的shell
#!/bin/bash
while read line;do
  userid=$(echo $line|cut -d: -f3)
  username=$(echo $line|cut -d: -f1)
  usershell=$(echo $line|cut -d: -f7)

if [ $[$userid%2] -eq 0 ];then
  echo "$username,$userid,$usershell"
fi
done < /etc/passwd

示例2:扫描/etc/passwd文件每一行,如发现GECOS字段为空,则填充用户名和单位电话为62985600,并提示该用户的GECOS信息修改成功
#!/bin/bash
#
while read line;do
  string=$(echo $line| cut -d: -f5)
  username=$(echo $line| cut -d: -f1 )
  if [[ -z "$string" ]];then
    chfn -f "$username" $username &> /dev/null
    chfn -p "62985600" $username &> /dev/null
    echo "$username 用户信息添加完成"
  else
    echo "$username messages exits"
  fi
done < /etc/passwd

bash中循环执行until语句
  until循环和wehile循环相反,until循环时只要条件判断式不成立则进行循环,并执行行循环程序。一但循环条件成立,则终止循环。
  until语法:
   until CONDITION; do
      循环体
    done

  循环进入条件:CONDITION为false
  循环退出条件:CONDITION为true

示例:求100以内所正整数之和
#!/bin/bash
#
declare -i i=1
declare -i sum=0

until [ $i -gt 100 ]; do
  let sum+=$i
  let i++
done
echo "Sum: $sum"

示例:打印九九乘法表
#!/bin/bash
#
declare -i j=1
declare -i i=1
until [ $j -gt 9 ]; do
  until [ $i -gt $j ]; do
    echo -n -e "${i}X${j}=$[$i*$j]\t"
    let i++
  done
  echo
  let i=1
  let j++
done

  创建无线循环
  while true; do
    循环体
  done

  until false; do
    循环体
 done

循环控制语句(用于循环体中):
continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断
while CONDTIITON1; do
  CMD1
  ...
  if CONDITION2; then
    continue
  fi
  CMDn
  ...
done

break [N]:提前结束循环;
while CONDTIITON1; do
  CMD1
  ...
  if CONDITION2; then
    break
  fi
  CMDn
  ...
done

示例1:求100以内所有偶数之和;要求循环遍历100以内的所正整数;
#!/bin/bash
#
declare -i i=0
declare -i sum=0
until [ $i -gt 100 ]; do
  let i++
  if [ $[$i%2] -eq 1 ]; then
    continue
  fi
  let sum+=$i
done
echo "Even sum: $sum"

示例2:每隔3秒钟到系统上获取已经登录的用户的信息;如果docker登录了,则记录于日志中,并退出;
#!/bin/bash
#
read -p "Enter a user name: " username
while true; do
  if who | grep "^$username" &> /dev/null; then
    break
  fi
  sleep 3
done
echo "$username logged on." >> /tmp/user.log

第二种实现:
#!/bin/bash
#
read -p "Enter a user name: " username
until who | grep "^$username" &> /dev/null; do
  sleep 3
done
echo "$username logged on." >> /tmp/user.log

for练习题:
1、添加10个用户user1-user10,密码同用户名

2、/etc/rc.d/rc3.d目录下分别有多个以K开头和以S开头的文件;分别读取每个文件,以K开头的文件输出为文件加stop,以S开头的文件输出为文件名加start

3、写一个脚本,提示输入正整数n的值,计算1+2+3+…n的总和

4、写一个脚本,提示请输入网络地址,如192.168.0.0,判断输入的网段中主机在线状态
#!/bin/bash
#authoe:tao
#describtion:给定一个网络地址,并探测此网络中主机的在线状态,并统计出在线主机和离线主机的个数

read -p "请输入您的IP地址: " IP

declare -i i=0
declare -i j=0

address=$(echo $IP |egrep "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])")
arg3=$(echo $IP |cut -d. -f1-3).

if [[ -z "$IP" ]];then
  echo "At least one IPv4." && exit 1
elif [[ "$IP" == "$address" ]];then
  for arg4 in $(seq 100 110);do
    if ping -c1 -W1 $arg3$arg4 &> /dev/null ;then
      echo -e "$arg3$arg4\tup."
      let i++
    else
      echo -e "$arg3$arg4\tdown."
      let j++
    fi
  done
else
  echo "您输入的格式不正确,请重新输入!" && exit 2
fi

echo "在线主机的个数为:$i"
echo "不在线主机的个数为:$j"

#=================================================================
# 执行结果如下:
[root@centos7 bin]# bash ping.sh
请输入您的IP地址: 192.168.1.1
192.168.1.100 up.
192.168.1.101 up.
192.168.1.102 up.
192.168.1.103 down.
192.168.1.104 down.
192.168.1.105 down.
192.168.1.106 up.
192.168.1.107 down.
192.168.1.108 down.
192.168.1.109 down.
192.168.1.110 down.
在线主机的个数为:4
不在线主机的个数为:7

while练习题
1、通过ping命令探测172.16.250.1-254范围内的所有主机的在线状态,统计在线主机和离线主机各多少。

2、打印九九乘法表

3、利用变量RANDOM生成10个随机数字,输出这个10数字,并显示其中的最大者和最小者

4、打印国际象棋棋盘

until练习题
1、每隔3秒钟到系统上获取已经登录的用户的信息;如果发现用户hacker登录,则将登录时间和主机记录于日志/var/log/login.log中,并提示该用户退出系统

2、随机生成10以内的数字,实现猜字游戏,提示比较大或小,相等则退出

 

猜你喜欢

转载自www.cnblogs.com/Link-Luck/p/9854971.html