bash脚本进阶

 shell脚本流程控制

    1.if语句

单分支:
if 判断条件;
 then
 
双分支:
if 判断条件; then
    条件为真的分支代码
else
    条件为假的分支代码
fi
 多分支:
if 判断条件;then
    条件为真的分支代码
elif 判断条件;then
    条件为真的分支代码
elif 判断条件;then
    条件为真的分支代码
else
    不符合以上条件的代码段
fi

例:写一个脚本/root/bin/argsnum.sh,接受一个文件路径作为参数;如果参数个数小于1,则提示用户“至少应该给一个
参数”,并立即退出;如果参数个数不小于1,则显示第一个参数所指向的文件中的空白行数
#!/bin/bash
if [ $# -lt 1 ];then
        echo "至少输入一个参数!"
        exit
else
        if [ -f $1 ];then
                echo "$1的空白行数为:`sed -n '/^$/p' $1 | wc -l`"
        else
                echo "你输入的第一个参数不是文件!"
                exit
        fi
fi

2、case语句

当条件的选择有多种时,if语句虽然能完成条件判断,但是代码不简洁,这时就可以选用case语句

语法:
case 变量 in
PAT1)
    分支1
    ;;
PAT2)
    分支2
    ;;
PAT3)
    分支3
    ;;
*)
    默认分支
    ;;
esac

case支持glob风格的通配符:
*: 任意长度任意字符
?: 任意单个字符
[]:指定范围内的任意单个字符
a|b: a或b

例:写一个脚本/root/bin/yesorno.sh,提示用户输入yes或no,并判断用户输入的是yes还是no,或是其它信息
#!/bin/bash
echo -e  "Input yes or no!"
read -s input
input=`echo "$input" | tr 'A-Z' 'a-z'`
case $input in
"yes"|"y")
 echo "The input is yes"
 ;;
"no"|"n")
 echo "The input is no"
 ;;
*)
 echo "$input"
 ;;
esac

3、for循环

for 变量名 in 列表;
    do
        循环体
    done
执行机制:
依次将列表中的元素赋值给“变量名” ; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束

例:用for循环打印99乘法表
#!/bin/bash
line=1
for i in `seq 1 9`;
        do
                for k in `seq 1 $i`;
                        do
                         echo -ne "${k}*${i}"="$[$k*$i]\t"
                        done
                 echo 
        done

4、while循环

while CONDITION; 
    do
        循环体
    done
 
CONDITION:循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;条件为“ true”,则执行一次循环;
           直到条件测试状态为“ false”终止循环
 
因此: CONDTION一般应该有循环控制变量;而此变量的值会在循环体不断地被修正

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

例:while实现打印99乘法表
#!/bin/bash
i=1
while [ $i -le 9 ];
 do
  j=1
  while [ $j -le $i ];
   do
    echo -ne "${j}x${i}=$[$j*$i]\t"
    ((j++))
   done
  echo 
  ((i++))
 done

5、until循环

        until循环和while循环相反,while循环当条件判断是为真时执行,为假跳出循环。until循环则当条件判断为假时执行,为真时跳出循环

until CONDITION; 
    do
        循环体
    done
 
进入条件: CONDITION 为false
退出条件: CONDITION 为true

6、循环控制语句continue和break

continue为跳出这一次循环,continue#为提前结束第#层的本轮循环,而直接进入下一轮判断;最内层为第1层
break为跳出这一层循环体,break#为结束第n层循环体;最内层为一层

7、创建无限循环

主要运用while和until的特性,使用while true;或者until false使循环体一直运行
一般使用exit和循环控制语句break跳出循环

8、特殊用法

while循环的特殊用法(遍历文件的每一行):

while read line; 
    do
        循环体
    done < /PATH/FROM/SOMEFILE
依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量line

例:扫描/etc/passwd文件每一行,如发现GECOS字段为空,则填充用户名和单位电话为62985600,并提示该用户的GECOS信息修改成功。
#!/bin/bash
while read line;
 do
  note=`echo "$line" | cut -d: -f5`
  usr_name=`echo $line | cut -d: -f1`
  [[ -z $note ]] && chfn -h 62985600 -f 62985600 $usr_name &> /dev/null && echo "$usr_name  GECOS 信息修改成功!" 
 done < /etc/passwd

for循环的特殊格式:

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

类似C语言语法:
双小括号方法,即((…))格式,也可以用于算术运算

#I=10
#((I++))

9、select循环

        select循环用来实现shell程序中的交互,能显示菜单模式的输出,显示PS3提示符,等待用户输入,

        用户输入菜单中的摸个数字,执行相应命令

        用户输入被保存在REPLY变量中

select variable in list
    do
        循环体命令
    done

select 是个无限循环,因此要记住用 break 命令退出循环,或用 exit 命令终止脚本。也可以按 ctrl+c退出循环。
select 经常和 case 联合使用与 for 循环类似,可以省略 in list , 此时使用位置参量

例:输出UID大于等于1000的用户,等待用户输入菜单选项,输出对应的用户的passwd字段
#!/bin/bash

echo "UID大于等于1000的用户列表:"
select i in `cat /etc/passwd | cut -d: -f1,3 |sort -n -t: -k2 |awk -F: '{if($2>=1000) print $0;}'`;
 do
  name=`echo "$i" | cut -d: -f1`
  grep "^$name:" /etc/passwd 
 done

003.png


shell脚本函数

 介绍: 

        函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程。
它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一部分。
函数和shell程序比较相似,区别在于:Shell程序在子Shell中运行而Shell函数在当前Shell中运行。
因此在当前Shell中,函数可以对shell中变量进行修改

函数由两部分组成:函数名和函数体。

 
语法一:
function f_name {
...
函数体
...
}
 
语法二:
f_name() {
...
函数体
...
}

函数的使用:
可在交互式环境下定义函数
可将函数放在脚本文件中作为它的一部分
可放在只包含函数的单独文件中
函数必须先定义,再调用
 
调用:函数只有被调用才会执行;
调用:给定函数名出现的地方,会被自动替换为函数代码
 函数的生命周期:被调用时创建,返回时终止
 
 
函数有两种返回值:
 
函数的执行结果返回值:
(1) 使用echo或printf命令进行输出:
(2) 函数体中调用命令的输出结果
 
函数的退出状态码:
(1) 默认取决于函数中执行的最后一条命令的退出状态码
(2) 自定义退出状态码, 其格式为:return 从函数中返回,用最后状态命令决定返回值
return 0 无错误返回。
return 1-255 有错误返回

函数可以接受参数:

传递参数给函数:调用函数时,在函数名后面以空白分隔给定参数列表即可;例如“ testfunc arg1 arg2 ...”
在函数体中当中,可使用$1, $2, ...调用这些参数;还可以使用$@, $*, $#等特殊变量

变量作用域:

环境变量:当前shell和子shell有效
本地变量:只在当前shell进程有效,为执行脚本会启动专用子shell进程;因此,本地变量的作用范围是当前shell脚本程序文件,包括脚本中的函数。
局部变量:函数的生命周期;函数结束时变量被自动销毁
    注意:如果函数中有局部变量,如果其名称同本地变量, 使用局部变量。
    在函数中定义局部变量的方法
    local NAME=VALUE

函数递归:

函数直接或间接调用自身
注意递归层数

例:
示例:求5的阶层 
fact.sh
#!/bin/bash
#
fact() {
if [ $1 -eq 0 -o $1 -eq 1 ]; then
    echo 1
else
    echo $[$1*$(fact $[$1-1])]
fi

}
fact 5

   


 作业、练习

1、写一个脚本,用循环显示三角形

[root@ _161_ ~/bin/8-17_study]# cat dao_san_jiao.sh 
#!/bin/bash
read -t 30 -p "Input num:" num
for i in `seq 1 $num`;
 do
  line=""
  black=$[(2*$i)-1]
  space=$[$num-$i]
  for j in `seq 1 $space`;
   do
    line=$line" "
   done
  
  for k in `seq 1 $black`
   do
    line=$line"*"
   done
  echo "$line"
 done
[root@ _162_ ~/bin/8-17_study]# bash dao_san_jiao.sh
Input num:5
    *
   ***
  *****
 *******
*********

 2、用until循环实现国际象棋棋盘

[root@ _165_ ~/bin/8-17_study]# cat chess_until.sh 
#!/bin/bash
black="\033[47m  \033[0m"
white="\033[40m  \033[0m"
i=1
until [ $i -gt 8 ];
 do
  j=1
  until [ $j -gt 8 ];
   do
    [ $[($i+$j)%2] -eq 0 ] && echo -ne "$white"
    [ $[($i+$j)%2] -ne 0 ] && echo -ne "$black"
    ((j++))
   done
  echo
  ((i++))
 done

004.png

3、写一个脚本/root/bin/filetype.sh,判断用户输入文件路径,显示其文件类型(普通,目录,链接,其它文件类型)

[root@ _177_ ~/bin]# cat 8-15_study/filetype.sh 
#!/bin/bash
read -t 30 -p "Input file!:" filename
echo $filename
[ -z $filename ] && echo "你没有输入文件名!" && exit
! [ -a $filename ] &&  echo "你输入的文件不存在!" && exit

if [ -f $filename ];then
 [ -h $filename ] && echo "$filename is link file!" || echo "$filename is file!"
elif [ -d $filename ];then
 echo "$filename is directory!"
else
 echo "${filename} is other file!"
fi
[root@ _178_ ~/bin]# bash 8-15_study/filetype.sh 
Input file!:e
e
你输入的文件不存在!
[root@ _179_ ~/bin]# bash 8-15_study/filetype.sh 
Input file!:/etc/issue
/etc/issue
/etc/issue is file!

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

[root@ _181_ ~/bin]# cat 8-15_study/chkint.sh 
#!/bin/bash
read -t 30 -p "输入一个数:" num
[[ $num == "0" ]] && echo "你输入的数为0!" && exit
expr $num + 0 &>/dev/null
if [ $? -eq 0 ];then
 if [ $num -gt 0 ];then
  echo "你输入的数为正整数!"
 else
  echo "你输入的数为负整数!"
 fi
else
 echo "你输入的不是整数!"
fi
[root@ _182_ ~/bin]# bash 8-15_study/chkint.sh
输入一个数:e
你输入的不是整数!
[root@ _183_ ~/bin]# bash 8-15_study/chkint.sh
输入一个数:4.5
你输入的不是整数!
[root@ _184_ ~/bin]# bash 8-15_study/chkint.sh
输入一个数:3
你输入的数为正整数!
[root@ _185_ ~/bin]# bash 8-15_study/chkint.sh
输入一个数:-2
你输入的数为负整数!

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

[root@ _186_ ~/bin]# cat 8-15_study/creatuser.sh 
#!/bin/bash
read -t 30 -p "Input username:" username
id $username &>/dev/null
if [ $? -eq 0 ];then
 echo "用户已存在"
else
 useradd $username
 [ $? -eq 0 ] && id $username && echo -e  "成功创建了\e[32;5m${username}\e[0m用户" || echo "输入了非法用户名!" 
fi
[root@ _187_ ~/bin]# bash 8-15_study/creatuser.sh 
Input username:cej
用户已存在
[root@ _188_ ~/bin]# bash 8-15_study/creatuser.sh 
Input username:cej2    
uid=1008(cej2) gid=1008(cej2) 组=1008(cej2)
成功创建了cej2用户

6、判断/var/目录下所有文件的类型

[root@ _191_ ~/bin]# cat for_script/var_type_pwd.sh 
#!/bin/bash
cd /var
for i in `ls -A /var`;
 do
  echo "$i is `file -b $i`" 
 done
[root@ _192_ ~/bin]# bash for_script/var_type_pwd.sh 
account is directory
adm is directory
cache is directory
crash is directory
db is directory
empty is directory
games is directory
gopher is directory
kerberos is directory
lib is directory
local is directory
lock is symbolic link to `../run/lock'
log is directory
mail is symbolic link to `spool/mail'
nis is directory
opt is directory
preserve is directory
run is symbolic link to `../run'
spool is directory
tmp is sticky directory
.updated is ASCII text
www is directory
yp is directory

7、添加10个用户user1-user10,密码同用户名

[root@ _196_ ~/bin/for_script]# cat user_add_1_10.sh 
#!/bin/bash
for i in `seq 1 10`;
 do
  id user$i &>/dev/null
  if [ $? -eq 0 ];then
   echo "user$i exist!"
  else
   useradd user$i
   echo "user$i" | passwd --stdin user$i &>/dev/null
   [ $? -eq 0 ] && echo "user$i create successful!" || echo "user$i create faild !" 
  fi
 done
[root@ _197_ ~/bin/for_script]# bash user_add_1_10.sh 
user1 create successful!
user2 create successful!
user3 create successful!
user4 create successful!
user5 create successful!
user6 create successful!
user7 create successful!
user8 create successful!
user9 create successful!
user10 create successful!

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

[root@ _200_ ~/bin/for_script]# cat K_S_Find.sh 
#!/bin/bash
K_num=0
S_num=0
cd /etc/rc.d/rc3.d
for i in `ls -A /etc/rc.d/rc3.d`;
 do
  if echo "$i" | grep  -q "^K";then
   ((K_num++))
   echo "$i stop"
  elif echo "$i" | grep  -q "^S";then
   ((S_num++))
   echo "$i start"
  else
   echo "$i"   
  fi
 done
echo "K:$K_num"
echo "S:$S_num"
[root@ _201_ ~/bin/for_script]# bash K_S_Find.sh 
K50netconsole stop
S10network start
K:1
S:1

 

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

[root@ _206_ ~/bin/for_script]# cat int_sum.sh 
#!/bin/bash
read -t 30 -p "Input int:" int_num
expr $int_num + 0 &>/dev/null
! [ $? -eq 0 ] && { echo "你输入了非数字或者零!";exit; }     #判断是否是整数
! [ $int_num -gt 0 ] && { echo "你输入的不是正数!" ;exit; }   #判断是否是正数
sum=0
for i in `seq 1 $int_num`;
 do
  let sum=${sum}+$i
 done
echo "sum is $sum"
[root@ _207_ ~/bin/for_script]# bash int_sum.sh 
Input int:100
sum is 5050

10、写一个脚本,提示请输入网络地址,如192.168.0.0,判断输入的网段中主机在线状态

[root@ _209_ ~/bin/for_script]# cat ping_for.sh 
#!/bin/bash
for i in `seq 76 99`;
 do
  ping -c 1 -w1 10.1.252.$i &>/dev/null
  if [ $? -eq 0 ];then
   echo -e "\033[33m10.1.0.$i is online!\033[0m"
  else
   echo  "10.1.0.$i is offline!"
  fi
 done
[root@ _210_ ~/bin/for_script]# bash ping_for.sh
10.1.0.76 is offline!
10.1.0.77 is online!
10.1.0.78 is offline!
10.1.0.79 is offline!
10.1.0.80 is offline!
10.1.0.81 is offline
...

 待续!

 

原创文章,作者:M20-1--孔祥文,如若转载,请注明出处:http://www.178linux.com/38002

(0)
M20-1--孔祥文M20-1--孔祥文
上一篇 2016-08-21
下一篇 2016-08-21

相关推荐

  • Linux软件包管理

    一、rpm包管理工具 1、rpm包简介 rpm是redhat的软件包管理器。rpm包管理器全称rpm: Redhat Package Manager RPM  Package Manager。它的软件包后缀也为.rpm。除此外,debian,deb文件有dpkg包管理器。 rpm包的命名方式: 源代码:name-version.tar.{gz|bz…

    Linux干货 2016-08-21
  • 测试 网络班23期

    123123123123

    Linux干货 2016-09-12
  • 文本处理工具之grep

                    文本处理工具之grep 一、grep的简介 1、grep是一个文本过滤器的工具,它根据用户指定的模式(pattern)对目标文本进行匹配检查,并将匹配的行打印到标准输出或输出重定向。 2、模式:由文本字符或正则表达式组成 3、正则表达式分…

    2017-05-06
  • python之psutil模块

    python 安装psutil 来实现获取系统信息  # yum -y install python*pip # yum -y groupinstall “Development Tools # yum -y install python34-devel.x86_64 # pip3 install –upgrade pip # pip3 inst…

    Linux干货 2017-03-08
  • Linux的终端类型

         序  终端的概念是由Ken Thompson提出的,是人机交互的接口。它是一种字符型设备,有多种类型,它大体上分为设备终端,物理终端,虚拟终端,图形终端,串行终端,伪终端。 一、   设备终端  设备终端就是显而易见的外在设备,比如键盘、鼠标和显示器等。 二、&nbsp…

    Linux干货 2016-10-19
  • Javascript 装载和执行

    一两个月前在淘宝内网里看到一个优化Javascript代码的竞赛,发现有不少的人对Javascript的执行和装载的基础并不懂,所以,从那天起我就想写一篇文章,但一直耽搁了。自上篇《浏览器渲染原理简介》,正好也可以承前启后。 首先,我想说一下Javascript的装载和执行。通常来说,浏览器对于Javascript的运行有两大特性:1)载入后马上执行,2)执…

    Linux干货 2016-08-15

评论列表(1条)

  • 马哥教育
    马哥教育 2016-08-22 09:19

    文章对选择语句和循环语句的语法总结的很详细,同时也通过相应的示例加深了自己对脚本的理解,脚本的学习没有什么捷径。靠平常的积累,平时写的多了,看的多了,我们才能运用自如,我们可以在平时的写作中尝试用不同的语法总结出他们的不同点和最适用的场景哦。