Linux shell脚本条件循环

while 循环和 until 循环的应用实践

循环语句命令常用于重复执行一条指令或一组指令,直到条件不再满足时停止,Shell脚本语言的循环语句常见的有while、until、for及select循环语句。

while 循环语句主要用来重复执行一组命令或语句,在企业实际应用中,常用于守护进程或持续运行的程序,除此以外,大多数循环都会用后文即将讲解的for循环语句。

1 当型和直到型循环语法

1.1 while 循环语句

while 循环语句的基本语法为:

复制代码
while <条件表达式>
do
  指令...
done

提示:注意代码缩进。

while 循环语句会对紧跟在while命令后的条件表达式进行判断:

  • 如果该条件表达式成立,则执行while 循环体里的命令或语句(即语法中do和done之间的指令),每一次执行到done时就会重新判断while条件表达式是否成立,直到条件表达式不成立时才会跳出while 循环体。
  • 如果一开始条件表达式就不成立,那么程序就不会进入循环体(即语法中do和done之间的部分)中执行命令了。

为了便于大家记忆,下面是某女生写的while条件语句的中文形象描述:

bash 复制代码
# 如果男朋友努力工作,则继续相处
while 男朋友努力工作
do
  继续相处
done

while 循环执行流程对应的逻辑图如下:

1.2 until 循环语句

until 循环语句的语法为:

bash 复制代码
until <条件表达式>
do
  指令...
done

until 循环语句的用法与while 循环语句的用法类似,区别是until会在条件表达式不成立时,进入循环执行指令;条件表达式成立时,终止循环。

为了便于大家记忆,下面是某女生写的until条件语句的中文形象描述:

bash 复制代码
#如果男朋友不努力工作,就不继续相处
until 男朋友不努力工作
do
  继续相处 
done

until 循环执行流程对应的逻辑图如下:

2 当型和直到型循环的基本范例

示例1: 竖向打印54321

while格式:

bash 复制代码
#!/bin/bash
i=5
while ((i>0))
do
  echo $i
  ((i--))
done

until格式:

bash 复制代码
#!/bin/bash
i=5
until ((i==0))
do
  echo $i
  ((i--))
done

示例2: 计算1+2+3+...+99+100的和

bash 复制代码
#!/bin/bash
i=1
sum=0
while ((i<=100))
do
  ((sum+=i))
  # let sum=sum+i
  ((i++))
  # let i++
done
echo "1+2+3+...+99+100=$sum"

示例3: 计算5的阶乘

bash 复制代码
#!/bin/bash
i=1
sum=1
while ((i<=5))
do
  ((sum*=i))
  ((i++))
done
echo "5的阶乘为:$sum"

示例4: 猴子吃桃。

  • 猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个。
  • 第二天早上又将第一天剩下的桃子吃掉一半,又多吃了一个。
  • 以后每天早上都吃了前一天剩下的一半零一个。
  • 到第 10 天早上想再吃时,发现只剩下一个桃子了。

问:一共有多少个桃子?

解题方法1: while 循环9次

bash 复制代码
#!/bin/bash
# 当天桃子数量,第一天为1
today=1
# 前一天桃子数量
lastday=0
# 只需要迭代9次
i=1
while ((i<=9))
do
  # 计算上一天桃子数量
  lastday=$[(today+1)*2]
  # 把上一天的数量当作今天的数量
  today=${lastday}
  ((i++))
done
echo "猴子第一天摘的桃子数量是:$today。"

解题方法2: 函数递归调用

bash 复制代码
#!/bin/bash
function sum (){
  if [[ $1 = 1 ]];then
    echo $1
  else
     echo $[ ($(sum $[$1 -1]) + 1)*2 ]
  fi
}
echo "猴子第一天摘的桃子数量是:$(sum 10)。"

示例5: 系统随机产生一个50以内数字,猜出该数字,并记录猜测次数。

bash 复制代码
#!/bin/bash
# 生成随机数字,并保存到文件/tmp/number
random_num=$[ RANDOM%50+1 ]
echo "${random_num}" >> /tmp/number

# 记录猜测次数
i=0
while true
do
  read -p "猜一猜系统产生的50以内随机数是:" num
  if ((num>=1 && num<=50));then
    ((i++))
    if [ $num -eq ${random_num} ];then
      echo "恭喜你,第$i次猜对了!"
      # 清理临时文件
      rm -f /tmp/number
      exit
    else
      echo -n "第$i次猜测,加油。"
      [ $num -gt ${random_num} ] && echo "太大了,往小猜。" || echo "太小了,往大猜。"
    fi
  else
    echo "请输入一个介于1-50之间的数字。"
  fi 
done

在实际工作中,一般会通过客户端SSH连接服务器,因此可能就会有在脚本或命令执行期间不能中断的需求,若中断,则会前功尽弃,更要命的是会破坏系统数据。

下面是防止脚本执行中断的几个可行方法:

  1. 使用sh /server/scripts/while_01.sh & 命令,即使用 & 在后台运行脚本。

  2. 使用nohup /server/scripts/uptime.sh &命令,即使用 nohup 加&在后台运行脚本。

  3. 利用 screen 保持会话,然后再执行命令或脚本,即使用 screen 保持当前会话状态。

3 让 Shell 脚本在后台运行的知识

脚本运行的相关用法和说明:

  • sh whilel.sh &,把脚本whilel.sh放到后台执行(在后台运行脚本时常用的方法)。
  • ctl+c,停止执行当前脚本或任务。
  • ctl+z,暂停执行当前脚本或任务。
  • bg,把当前脚本或任务放到后台执行。bg可以理解为background。
  • fg,把当前脚本或任务放到前台执行,如果有多个任务,可以使用fg加任务编号调出对应的脚本任务,如fg 2,调出第二个脚本任务。fg可以理解为frontground。
  • jobs,查看当前执行的脚本或任务。
  • kill,关闭执行的脚本任务,即以"kill %任务编号"的形式关闭脚本。任务编号,可以通过jobs命令获得。

示例1: 让所有 CPU 满负荷工作

bash 复制代码
#!/bin/bash
cpu_count=$(lscpu|grep '^CPU(s)'|awk '{print $2}')
i=1
while ((i<=${cpu_count}))
do
  {
   while :
   do
     ((1+1))
   done
   } &
 ((i++))
done

注意:

  • { comand1;command2;command3; ...; } &,将多个命令放到后台运行
  • {} 内部两侧有空格。
  • 最后一个命令后有分号。
并发控制

示例1: 消耗完所有CPU

bash 复制代码
[wsh@test ~ ✔]$ vim cpu_load 
#!/bin/bash
while true
do
  ((1+1))
done

[wsh@test ~ ✔]$ vim multi_cpu_load
#!/bin/bash
cpu_count=$(lscpu | awk '/^CPU\(s\):/ { print $2}')
i=1
while [ $i -le 2 ]
do
  bash /home/wsh/cpu_load &
  ((i++))
done

示例2: 控制并发数量不能超过CPU数量

bash 复制代码
[wsh@test ~ ✔]$ vim cpu_load
#!/bin/bash
while true
do
  ((1+1))
done &

# 只允许改进程运行10s
pid=$!
sleep 10 && kill -9 $pid

[wsh@test ~ ✔]$ vim multi_cpu_load
#!/bin/bash
cpu_count=$(lscpu | awk '/^CPU\(s\):/ { print $2}')
while true
do
  bash /home/wsh/cpu_load &
  # 控制并发数量不能大于cpu数量  
  while true
  do
    jobs=$(jobs -l |wc -l)
    if [ $jobs -ge $cpu_count ];then
      # 如果并发数大于cpu数量,则sleep 3s后继续检测
      sleep 3
    else
      # 如果并发数小于cpu数量,则退出当前while 循环的并发检测
      break
    fi
  done
done
wait 指令

等后台任务运行完成。

bash 复制代码
#!/bin/bash
> /tmp/sleep
i=1
while [ $i -le 10 ]
do
  ( sleep $i && echo sleep $i >> /tmp/sleep )&
  ((i++))
done
# 等待前面后台任务运行完成后再运行wait后指令
wait
cat /tmp/sleep

4 企业生产实战:while 循环语句实践

示例1: 每隔2秒输出一次系统负载(负载是系统性能的基础重要指标)情况。

bash 复制代码
[wsh@test ~ ✔]$ cat while1.sh 
#!/bin/bash
while true
do
  uptime
  sleep 2
done

[wsh@test ~ ✔]$ bash while1.sh
 20:38:57 up  5:06,  2 users,  load average: 0.00, 0.01, 0.05
 20:38:59 up  5:06,  2 users,  load average: 0.00, 0.01, 0.05
 20:39:01 up  5:06,  2 users,  load average: 0.00, 0.01, 0.05
 20:39:03 up  5:06,  2 users,  load average: 0.00, 0.01, 0.05
^C

# 按 ctrl+c 停止运行

[wsh@test ~ ✔]$ cat while2.sh 
#!/bin/bash
while true
do
  uptime >> /tmp/loadaverage.log
  sleep 2
done
# 放到后台运行
[wsh@test ~ ✔]$ bash while2.sh &
[wsh@test ~ ✔]$ tail -f while2.sh

示例2: 后台检测sshd服务,如果未运行,则重启sshd服务。

while格式:

bash 复制代码
#!/bin/bash
while true
do 
  systemctl is-active sshd.service &>/dev/null
  if [ $? -ne 0 ];then 
    systemctl restart sshd.service  &>/dev/null
  fi
  sleep 5
done

until格式:

bash 复制代码
#!/bin/bash
until false
do 
  systemctl is-active sshd.service &>/dev/null
  if [ $? -ne 0 ];then 
    systemctl restart sshd.service  &>/dev/null
  fi
  sleep 5
done

示例3: 使用while守护进程的方式监控网站,每隔3秒确定一次网站是否正常。

bash 复制代码
[wsh@test scripts ✔]$ cat check_url.sh 
#!/bin/bash

if [ $# -ne 1 ];then
  echo "Usage: $0 url"
  exit 1
fi

url="$1"

while true
do
  if curl -o /dev/null -s --connect-timeout 5 $url;then
    echo $url is ok.
  else
    echo $url is error.
  fi
  sleep 3
done

执行结果

bash 复制代码
[wsh@test scripts ✔]$ bash check_url.sh 
Usage: check_url.sh url

[wsh@test scripts ✔]$ bash check_url.sh www.baidu.com
www.baidu.com is ok.
www.baidu.com is ok.
^C

[wsh@test scripts ✔]$ bash check_url.sh www.baidu.co
www.baidu.co is error.
www.baidu.co is error.
^C

示例4: 手机发信息平台:

  • 每发一次短信(输出当前余额)花费1角5分钱
  • 当余额低于1角5分钱时就不能再发短信了,提示"余额不足,请充值"
  • 用户充值后可以继续发短信
bash 复制代码
#!/bin/bash

# 默认金额
money=0.5

# 保存消息的文件
msg_file=/tmp/message
# 清空消息文件
> $msg_file

# 手机操作菜单
function print_menu () {
  cat << EOF
1. 查询余额
2. 发送消息
3. 充值
4. 退出
EOF
}

# 检查数字函数
function check_digit () {
  expr $1 + 1 &> /dev/null && return 0 || return 1
}

# 显示余额函数
function check_money_all () {
    echo "余额为:$money。"
}

# 检查余额是否充足
function check_money () {
  new_money=$(echo "$money*100"|bc|cut -d . -f1)
  if [ ${new_money} -lt 15 ];then
    echo "余额不足,请充值。"
    # 余额不足,返回值为1
    return 1
  else
    # 余额充足,返回值为0
    return 0
  fi
}

# 充值函数
function chongzhi () {
  read -p "充值金额(单位:元):" chongzhi_money
  while true
  do
    check_digit $chongzhi_money
    if [ $? -eq 0 ] && [ ${chongzhi_money} -ge 1 ];then
      money=$( echo "($money+${chongzhi_money})"|bc)
      echo "当前余额为:$money"
      return 0
    else
      read -p "重新输入充值金额:" chongzhi_money 
    fi
  done
}

# 发送消息函数
function send_msg () {
  # 检查余额是否充足
  check_money
  # 返回值与0比较,判定余额是否充足
  if [ $? -eq 0 ];then
    read -p "msg: " message
    echo "$message" >> ${msg_file}

    # bc计算器计算的结果,如果值小于1,则前面的0省略
    new_money=$(echo "scale=2;($money*100-15)" | bc |cut -d. -f1 )
    if [ ${new_money} -ge 100 ];then
      money=$(echo "scale=2;${new_money}/100" | bc )
    else
      money=0$(echo "scale=2;${new_money}/100" | bc )
    fi
    echo "当前余额为:$money"
  fi
}

# 主程序
while true
do
  print_menu
  echo
  read -p "请输入你的选择:" choice
  clear
  case  $choice in
    1)
      check_money_all
      ;;
    2)
      send_msg
      ;;
    3)
      chongzhi
      ;;
    4)
      exit
      ;;
    *)
      echo "只能从1、2、3、4中选择。" 
      ;;
  esac
  echo
done

5 while 循环按行读文件的方式总结

示例:读取/etc/hosts内容

bash 复制代码
[wsh@test scripts ✔]$ cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
10.1.8.88 wsh-shell
  • 方式1:采用exec读取文件,然后进入while 循环处理。
bash 复制代码
#!/bin/bash
exec < /etc/hosts
while read line
do
  echo $line
done
  • 方式2:使用cat读取文件内容,然后通过管道进入while 循环处理。
bash 复制代码
#!/bin/bash
cat /etc/hosts | while read line
do
  echo $line
done
  • 方式3:在while 循环结尾done处通过输入重定向指定读取的文件。
bash 复制代码
#!/bin/bash
while read line
do
  echo $line
done < /etc/hosts
  • 方式4:定义shell分隔符为换行符
bash 复制代码
#!/bin/bash
IFS=$'\n'
for line in $(cat /etc/hosts)
do
  echo $line
done

6 while 循环和 until 循环小结

  1. while 循环结构及相关语句综合实践小结
    • while 循环的特长是执行守护进程,以及实现我们希望循环持续执行不退出的应用,适合用于频率小于1分钟的循环处理,其他的while 循环几乎都可以被后面即将要讲到的for循环及定时任务crond功能所替代。
    • case语句可以用if语句来替换,而在系统启动脚本时传入少量固定规则字符串的情况下,多用case语句,其他普通判断多用if语句。
    • 一句话场景下,if语句、for语句最常用,其次是while(守护进程)、case(服务启动脚本)。
  2. Shell脚本中各个语句的使用场景
    • 条件表达式,用于简短的条件判断及输出(文件是否存在,字符串是否为空等)
    • if取值判断,多用于不同值数量较少的情况。
    • for最常用于正常的循环处理中while多用于守护进程、无限循环(要加sleep和usleep控制频率)场景。
    • case多用于服务启动脚本中,打印菜单可用select语句,不过很少见,一般用cat的here文档方法来替代。
    • 函数的作用主要是使编码逻辑清晰,减少重复语句开发。

for 和 select 循环语句的应用实践

for循环语句和while循环语句类似,但for循环语句主要用于执行次数有限的循环,而不是用于守护进程及无限循环。for循环语句常见的语法有两种,下面将在不同的语法中对for循环语句进行详尽的讲解。

1 for 循环语法结构

1.1 变量取值型

第一种for循环语句语法结构如下:

bash 复制代码
for 变量名 in 变量取值列表
do
  指令...
done

在此结构中"in 变量取值列表"可以省略,省略时相当于in "$@",也就是使用 for i 就相当于使用for i in "$@"

这种for循环语句执行过程如下:

  1. for关键字后面会有一个"变量名",变量名依次获取in关键字后面的变量取值列表内容(以空格分隔)。
  2. 每次仅取一个,然后进入循环(do和done之间的部分)执行循环内的所有指令,当执行到done时结束本次循环。
  3. 之后,"变量名"再继续获取变量列表里的下一个变量值,继续执行循环内的所有指令,当执行到done时结束返回。
  4. 以此类推,直到取完变量列表里的最后一个值并进入循环执行到done结束为止。
1.2 C 语言型

第二种for循环语句称为C 语言型for循环,语法结构如下:

bash 复制代码
for ((expl;exp2;exp3))
do
  指令...
done

此种循环语句和while循环语句类似,但语法结构比while循环更规范、工整。

这种for循环语句说明如下:

  1. for关键字后的双括号内是三个表达式:

    • 第一个是变量初始化(例如:i=0)

    • 第二个为变量的范围(例如:i<100)

    • 第三个为变量自增或自减(例如:i++)。

  2. 当第一个表达式的初始值符合第二个变量的范围时,就进入循环执行;条件不满足时就退出循环。

for循环结构执行流程对应的逻辑图如下:

2 for 循环语句的基础实践

示例1:竖向升序打印
bash 复制代码
# 列表型
[wsh@test ~ ✔]$ vim for1.sh
#!/bin/bash
for i in {1..5}
do
  echo $i
done
[wsh@test ~ ✔]$ bash for1.sh 
1
2
3
4
5

[wsh@test ~ ✔]$ vim for2.sh
#!/bin/bash
for i in $(seq 5)
do
  echo $i
done

# c语言型
[wsh@test ~ ✔]$ vim for3.sh
#!/bin/bash
for ((i=1;i<=5;i++))
do
  echo $i
done

# while型
[wsh@test ~ ✔]$ vim while1.sh
#!/bin/bash
i=1
while ((i<=5)) 
do
  echo $i
  ((i++))
done
示例2:竖向降序打印
bash 复制代码
[wsh@test ~ ✔]$ vim for4.sh 
#!/bin/bash
for i in {5..1}
do
  echo $i
done
[wsh@test ~ ✔]$ bash for4.sh 
5
4
3
2
1

[wsh@test ~ ✔]$ vim for5.sh
#!/bin/bash
for i in $(seq 5 -1 1)
do
  echo $i
done
示例3:求和、求乘积
bash 复制代码
#!/bin/bash
sum=0
for i in {1..10}
do
  sum=$[ sum + i ]
done
echo $(seq -s '+' 10)=$sum
bash 复制代码
#!/bin/bash
sum=1
for i in {1..10}
do
  ((sum*=i))
done
echo $(seq -s '*' 10)=$sum
示例4:求水鲜花数

水仙花数是指一个 3 位数,它的每个位上的数字的 3次幂之和等于它本身。

bash 复制代码
#!/bin/bash
for num in {100..999}
do
  n1=$[num/100]
  n2=$[num%100/10]
  n3=$[num%10]
  if [ $[ n1**3 + n2**3 + n3**3 ] -eq $num ];then
    echo $n1^3 + $n2^3 + $n3^3 = $n1$n2$n3
  fi
done
示例5:九九乘法表
bash 复制代码
#!/bin/bash
for num1 in {1..9}
do
  for ((num2=1;num2<=num1;num2++))
  do
    echo -en "$num2*$num1=$[ num2 * num1 ]\t"
  done
  echo
done

3 for 循环语句的企业级案例

示例1: 批量修改文件扩展名(把txt改成jpg)

bash 复制代码
[wsh@test ~ ✔]$ touch file-{1..3}.txt
[wsh@test ~ ✔]$ ls file-{1..3}.txt
file-1.txt  file-2.txt  file-3.txt

# 获取新文件名方法
[wsh@test ~ ✔]$ echo file-3.txt|sed 's/\.txt$/.jpg/'
file-3.jpg
[wsh@test ~ ✔]$ echo $(echo file-3.txt|cut -d. -f1).jpg
file-3.jpg
[wsh@test ~ ✔]$ echo $(echo file-3.txt|awk -F. '{print $1}').jpg 

# 使用sed
[wsh@test ~ ✔]$ vim for6.sh
#!/bin/bash
for file in $(ls *.txt)
do
  new_name=$(echo $file|sed 's/.txt$/.jpg/')
  mv $file ${new_name}
done

[wsh@test ~ ✔]$ bash for6.sh 
[wsh@test ~ ✔]$ ls file*
file-1.jpg  file-2.jpg  file-3.jpg

# 还可以直接使用rename命令
[wsh@test ~ ✔]$ rename ".jpg" ".txt" *jpg
[wsh@test ~ ✔]$ ls *txt
file-1.txt  file-2.txt  file-3.txt

# rename还可用于删除文件名中特定字符串
[wsh@test ~ ✔]$ rename "file-" "" *txt
[wsh@test ~ ✔]$ ls *txt
1.txt  2.txt  3.txt  

示例2: 批量设置服务开启启动

bash 复制代码
#!/bin/bash
for service in crond atd chronyd 
do
  systemctl enable $service
done

示例3: 批量重启多个主机sshd服务

bash 复制代码
#!/bin/bash
for host in server{1..10}
do
  ssh root@$host systemctl restart sshd
done

示例4: 批量备份文件

bash 复制代码
#!/bin/bash
for file in host*
do
  cp $file /backup/${file}.bak;
done

4 for 循环语句的企业高级实战案例

bash 复制代码
# 准备mariadb数据库
[wsh@test scripts ✔]$ sudo yum install -y mariadb-server
[wsh@test scripts ✔]$ sudo systemctl start mariadb

示例1: 实现 MySQL库备份的脚本

创建数据库脚本

bash 复制代码
[wsh@test scripts ✔]$ vim create_dbs.sh
#!/bin/bash
MYSQL_CMD='mysql -uroot -e '
for db in web1 web2
do
 ${MYSQL_CMD} "create database $db;"
done

执行效果

bash 复制代码
[wsh@test scripts ✔]$ bash create_dbs.sh
[wsh@test scripts ✔]$ mysql -uroot -e 'show databases;'
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| test               |
| web1               |
| web2               |
+--------------------+

备份数据库脚本

bash 复制代码
[wsh@test scripts ✔]$ vim dump_dbs.sh
#!/bin/bash
MYSQL_CMD='mysqldump -uroot '
for db in web1 web2
do
 ${MYSQL_CMD} $db > $db.sql;
done

执行效果

bash 复制代码
[wsh@test scripts ✔]$ bash dump_dbs.sh
[wsh@test scripts ✔]$ ls *sql
web1.sql  web2.sql

示例2: 实现 MySQL分库分表备份的脚本

创建表脚本

bash 复制代码
[wsh@test scripts ✔]$ vim create_tables.sh
#!/bin/bash
MYSQL_CMD='mysql -uroot $db -e '
for db in web1 web2
do
 ${MYSQL_CMD} "create table test(id int,name varchar(16));insert into test values(1,'testdata')";
done

执行结果

bash 复制代码
[wsh@test scripts ✔]$ bash create_tables.sh
[wsh@test scripts ✔]$ mysql -uroot -e 'use web1;show tables; select * from test;'
+----------------+
| Tables_in_web1 |
+----------------+
| test           |
+----------------+
+------+----------+
| id   | name     |
+------+----------+
|    1 | testdata |
+------+----------+

备份表脚本

bash 复制代码
[wsh@test scripts ✔]$ vim dump_tables.sh
#!/bin/bash
MYSQL_CMD='mysqldump -uroot $db'
for db in web1 web2
do
 ${MYSQL_CMD} > ${db}_test.sql;
done

执行效果

bash 复制代码
[wsh@test scripts ✔]$ bash dump_tables.sh
[wsh@test scripts ✔]$ ls *test.sql
web1_test.sql  web2_test.sql

5 Linux 系统产生随机数的6种方法

方法1: 通过系统环境变量(RANDOM)实现

RANDOM的随机数范围为0~32767,因此加密性不是很好,可以通过在输出的随机数前后增加自定义字符串的方式解决,最后再一起执行md5sUm操作并截取结果的后n位,这样一来,就无法根据随机数范围0~32767来猜出具体结果了。

bash 复制代码
[wsh@test ~ ✘]$ echo "wsh$RANDOM"|md5sum |cut -c 1-8
d3a8a340

方法2: 通过openssl产生随机数,结果中包涵字母大小写和数字更加安全。

bash 复制代码
[wsh@test ~ ✔]$ openssl rand -base64 8
Szp2fJ/C7tk=
[wsh@test ~ ✔]$ openssl rand -base64 20
pIELp8BUyTtRIE3v/GNrAijQBLU=

方法3: 通过date命令获得随机数,%N输出纳秒(nanoseconds)。

bash 复制代码
[wsh@test ~ ✔]$ date +%s%N
1756039707120616853
[wsh@test ~ ✔]$ date +%s%N
1756039710419740788

方法4: 通过设备/dev/urandom或/dev/random和md5sum生成随机数。

这两个设备存储着系统当前运行的环境的实时数据。它可以看作是系统某个时候,唯一值数据,因此可以用作随机数元数据。我们可以通过文件读取方式,读得里面数据。

  • /dev/urandom,返回指定请求数量的随机数,如果请求的数量非常庞大的话,返回的随机数可能是伪随机数,随机数质量稍差些。即使如此,它们对大多数应用来说已经足够了。

  • /dev/urandom,也是返回指定请求数量的随机数,但是它产生的随机数质量很高, 是属于真随机数, 主要用于需要高质量的随机数的地方,比如:生成加密密钥等。

    为了保证随机数的质量,/dev/random只能返回熵池当前最大可用的随机二进制位,当请求超过这个值,就会阻塞,直到熵池中有足够的随机二进制位。

bash 复制代码
[wsh@test ~ ✔]$ head /dev/urandom |md5sum 
e0a43d0833d3c8431d45b01429734d33  -
[wsh@test ~ ✔]$ head /dev/urandom |md5sum 
f3b8a146781ea92335039464304d5166  -

方法5: 通过UUID生成随机数。

UUID码全称是通用唯一识别码(UniversallyUniqueIdentifier,UUID),它是一个软件建构的标准,亦为自由软件基金会(OpenSoftwareFoundation,OSF)的组织在分布式计算环境(DistributedComputingEnvironment,DCE)领域的一部分。

UUID的目的是让分布式系统中的所有元素都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。如此一来,每个人都可以创建不与其他人发生冲突的UUID。在这样的情况下,就不需要考虑数据库创建时的名称重复问题了。它会让网络中任何一台计算机所生成的UUID码都是互联网整个服务器网络中唯一的编码。它的原信息会加人硬件、时间、机器当前运行信息等。

bash 复制代码
[wsh@test ~ ✔]$ cat /proc/sys/kernel/random/uuid
7323c4ed-8256-45bd-a8f7-8e36e1e03a74
[wsh@test ~ ✔]$ uuidgen 
21258f54-61e2-46af-9abf-a4fbb34bbcd5

方法6: 使用expect附带的mkpasswd命令生成随机数。

bash 复制代码
[wsh@test ~ ✔]$ mkpasswd -l 20 -d 4 -c 4 -C 4 -s 4
0lyl!7aLnqU-;83[WQlk

选项说明:

  • -l,控制密码长度。
  • -d,控制数字字母最小数量。
  • -c,控制小写字母最小数量。
  • -C,控制大写字母最小数量。
  • -s,控制特殊符号最小数量。

方法7: 使用mktemp命令和sha1sum生成随机数。

bash 复制代码
[wsh@test ~ ✔]$ mktemp -u 
/tmp/tmp.3XfWcblZDc
[wsh@test ~ ✔]$ mktemp -u |sha1sum 
740fdd67d699071126c52daa1a7d5cf2c0e3edb5  -

为了让随机数统一,可以统一使用md5sum或者sha1sum等校验命令处理。

6 select 循环语句介绍及语法

select循环语句的主要作用可能就是创建菜单。

在执行带select循环语句的脚本时,输出会按照数字顺序的列表显示一个菜单项,并显示提示符(默认是#?),同时等待用户输入数字进行选择。

下面就来带大家看看生成菜单项的语法及具体案例实践。

第一种for循环语句为变量取值型,语法结构如下:

bash 复制代码
select 变量名 [ in 菜单取值列表 ]
do
  指令...
done

此结构中"in 变量取值列表"可省略,省略时相当于使用 in "$@",使用for i就相当于使用for i in "$@"

在这种select循环语句的语法中,select关键字后面会有一个"变量名":

  • 每次执行时,变量名从in关键字后面的变量取值列表中(以空格分隔)取一个,然后进人循环(do和done之间),执行循环内的所有指令,当执行到done时结束返回。
  • select循环执行后会出现菜单项等待用户选择(不会自动循环所有变量列表),而用户输人的只能是菜单项前面的数字序号,每输入一次对应的序号就会执行一次循环。
  • 之后"变量名"再继续取变量列表中取一个值,继续执行循环内的所有指令(do和done之间的指令),当执行到done时结束返回。
  • 以此类推。
  • select是一个没有终止的循环,除非选择某个值时执行了退出循环指令,才会退出select循环;否则select需要会一直执行下去。
  • select如何遍历列表,完全取决于用户的选择,而for、while、until等是按顺序遍历列表。

select循环结构执行流程对应的逻辑图如下:

7 select 循环语句案例

示例1: 打印课程清单

bash 复制代码
[wsh@test ~ ✔]$ vim select1.sh
#!/bin/bash
select course in mysql python linux
do
  echo $course
done

[wsh@test ~ ✔]$ bash select1.sh 
1) mysql
2) python
3) linux
#? 1
mysql
#? 2
python
#? 1
mysql
#? 1
mysql
#? 3
linux
#? ^C
# 按ctrl+c退出。

用while和case实现上面的select语句

bash 复制代码
#!/bin/bash

function menu () {
echo -n '1) mysql
2) python
3) linux
#? '
}

menu
read choice

while true
do
  case $choice in
  1)
    echo mysql
    read -p '#? ' choice
    ;;
  2)
    echo python
    read -p '#? ' choice
    ;;
  3)
    echo linux
    read -p '#? ' choice
    ;;
  '')
    menu
    read choice
    ;;
  *)
    echo
    read -p '#? ' choice
    ;;
  esac
done

示例2: 更改提示符,并配合case和exit使用

环境变量:

  • PS3,是select的选择提示符。
  • REPLY,是select选择的对应序号。
bash 复制代码
[wsh@test ~ ✔]$ cat select2.sh
#!/bin/bash
PS3="选择你的课程: "
select course in linux mysql python exit
do
  echo "你选择了第 $REPLY 个条目。"
  case $course in
    linux)
      echo linux xxx
      ;;
    mysql)
      echo mysql yyy
      ;;
    python)
      echo python zzz
      ;;
    exit)
      exit
      ;;
  esac
  echo
done

# 执行结果如下
[wsh@test ~ ✔]$ bash select2.sh 
1) linux
2) mysql
3) python
4) exit
选择你的课程: 1
你选择了第 1 个条目。
linux xxx

选择你的课程: 2
你选择了第 2 个条目。
mysql yyy

选择你的课程: 1
你选择了第 1 个条目。
linux xxx

选择你的课程: 4
你选择了第 4 个条目。

示例3: 两个数计算器

bash 复制代码
[wsh@test ~ ✔]$ vim select3.sh 
#! /bin/bash
num1=10
num2=20
echo There are two numbers $num2 and $num1 。
select method in 加法 减法 乘法 退出
do
  case $method in
    加法)
      echo $[ num1 + num2 ]
      ;;
    减法)
      echo $[ num2 - num1 ]
      ;;
    乘法)
      echo $[ num1 * num2 ]
      ;;
    退出)
      exit
      ;;
    *)
      echo 从1-4中选择
      ;;
  esac
done

# 执行效果
[wsh@test ~ ✔]$ bash select3.sh
There are two numbers 20 and 10 。
1) 加法
2) 减法
3) 乘法
4) 退出
#? 1
30
#? 2
10
#? 3
200
#? 4