bash(2)

 

前言:source和“.” 与 bash 和 ./scriptName的区别在于:source和“.”会将脚本的语句在父shell进程中运行,而其他的都会在子shell中运行。

  • 举个例子:在一个脚本中,脚本名为2.sh定义一个变量,且变量的值是一个变量的返回值,脚本如下:
var=$(uname -r)
  • 执行脚本
[root@6 yhy]# bash 2.sh
[root@6 yhy]# echo $var

[root@6 yhy]# . 2.sh
[root@6 yhy]# echo $var
2.6.32-504.el6.x86_64

前言:set可以显示系统当前的本地变量和环境变量,unset可以取消系统的本地变量和环境变量

变量定义也有一定规范
# 变量命名规则:变量名首字母必须为字母(a-z A-Z),下划线(_),或者美元符号($)开始。(有些编译器已经支持中文变量名了)
变量名只能是字母(a-z A-Z),数字(0-9),下划线(_)的组合,并且之间不能包含空格,数字不能放在变量名首位。
变量名不能使用编程语言的保留字。比如在javascript中不能使用true,false,while,case,break保留字等等。

(一)bash编程权威指南6

前言:case语句作为bash shell的另外一种循环应用很广泛,我们会发现在很多服务的启动脚本中都出现了case语句的身影。那为什么case语句的应用场景经常出现在服务的启动脚本中呢?原因是:因为对应服务启动脚本而言,都会接受一些诸如:start、stop、status、restart、reload等位置参数,那么这些不同的位置经过case语句的判断之后,会执行不同的代码块,这个功用就是case语句之所以应用广泛的原因

  • 示例:使用if else 实现用户的选择
示例:显示一个菜单给用户
cpu)display cpu information
mem)display memory information
disk)display disk information
quit)quit
1)提示用户给出自己的选择
2)正确的选择给出相应的信息,否则,提示用户,让用户重新选择正确的选项,直到用户选择正确为止
cat << EOF
cpu)display cpu information
mem)display memory information
disk)display disk information
quit)quit
=============================
EOF

read -p "Enter your option:" option

# 这里的while,通过read命令循环输入option,使得循环可以继续进行
while [ "$option" != "cpu" -a "$option" != "mem" -a "$option" != "disk" -a "$option" != "quit" ]; do
echo "please input cpu , mem , disk , quit "
read -p "Enter your option:" option
done

if [ "$option" == "cpu" ];then
lscpu
elif [ "$option" == "mem" ];then
free -m
elif [ "$option" == "disk" ];then
fdisk -l /dev/sd[a-z]
elif [ "$option" == "quit" ];then
echo "quit"
exit 0
fi
  • case语句的语法格式
case $变量 in
PAT1)
分支1
;;
PAT2)
分支2
;;
PAT3)
分支3
;;
....
....
....
PATn)
分支n
;;
esac

  • 易错点分析(我经常忘记写分号,使得脚本出错)
注意: 
1)每一个分支的两个分号一定不能少,如果没有两个分号,那么每一个case 的分支都会执行
2)虽然这里的PAT可以只用模式匹配,但是只是支持glob模式的匹配,有:* ? [ ] [^ ] a|b 这几种方式
  • 将上述题目if else示例改写为case的判断,会使得代码的可读性增强
#/bin/bash
# description
# author

cat << EOF
cpu)display cpu information
mem)display memory information
disk)display disk information
quit)quit
=============================
EOF

read -p "Enter your option:" option

# 这里的while,通过read命令循环输入option,使得循环可以继续进行
while [ "$option" != "cpu" -a "$option" != "mem" -a "$option" != "disk" -a "$option" != "quit" ]; do
echo "please input cpu , mem , disk , quit "
read -p "Enter your option:" option
done

case $option in
cpu)
lscpu ;;
mem)
free -m ;;
disk)
fdisk -l /dev/sd[a-z] ;;
*)
echo "quit"
exit 0 ;;
esac
  • 练习:写一个服务框架脚本名为create_file,接受start 、stop、 restart、 status 四个参数之一 (这个题非常重要) 
    • 1:如果参数非此四者,则提示使用帮助后退出
    • 2:如果传递start参数,则在/var/log目录下创建一个与脚本名相同的文件,并显示启动。
    • 3:如果传递stop参数,就在/var/log目录下删除一个与脚本名相同的文件 ,并显示停止。
    • 4:如果传递restart参数,就先删除再创建此文件,而后显示重启完成。
    • 5:如果传递status参数, 如果文件存在,则显示running, 否则显示为stopped
#!/bin/bash
#
# chkconfig: - 50 50
# description: test service script

prog=$(basename $0)
FILE=/var/log/$prog

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

(二)bash编程权威指南7

前言:函数在bash shell编程中扮演了一个非常重要的角色,函数可以将重复代码进行封装,用到时再调用即可,这样使得在很大程度上避免了垃圾代码、重复代码
但是在bash shell 编程中,函数无法体现其强大之处。我们通常所说的函数式编程的思想无法在bash shell这里体现。如果有机会的话,我希望给大家介绍一下Python或JavaScript的函数式编程思想

  • 函数:代码重用 
    • 模块化编程 
      结构化编程 
      注意:定义函数的代码段不会自动执行,在调用时执行,所谓调用函数,在代码中给定函数名称即可,函数名出现的任何位置,在代码执行时候,都会自动替换为函数代码
  • 语法格式:
语法一:
function f_name{
函数体
}

语法二:
f_name(){
函数体
}
函数的生命周期:每次被调用时,被创建,返回时,终止

其状态返回结果为函数体中运行的最后一条命令的状态结果
自定义状态返回值,需要使用:return
return [0-255]
0: 成功
1-255:失败
  • 示例:给定一个用户名,取得用户名的ID号,和默认的shell
#!/bin/bash
#

userinfo() {
if id "$username" &> /dev/null; then
grep "^$username\>" /etc/passwd | cut -d: -f3,7
else
echo "No such user."
fi
}

username=$1
userinfo

username=$2
userinfo
  • 练习:写一个服务框架脚本名为create_file,接受start 、stop、 restart、 status 四个参数之一 (这次用函数实现) 
    • 1:如果参数非此四者,则提示使用帮助后退出
    • 2:如果传递start参数,则在/var/log目录下创建一个与脚本名相同的文件,并显示启动。
    • 3:如果传递stop参数,就在/var/log目录下删除一个与脚本名相同的文件 ,并显示停止。
    • 4:如果传递restart参数,就先删除再创建此文件,而后显示重启完成。
    • 5:如果传递status参数, 如果文件存在,则显示running, 否则显示为stopped
#!/bin/bash
#
# chkconfig: - 50 50
# description: test service script
#
prog=$(basename $0)
FILE=/var/log/$prog

start() {
if [ -f $FILE ]; then
echo "$prog is running yet."
else
touch $FILE
[ $? -eq 0 ] && echo "start $prog OK."
fi
}

stop() {
if [ -f $FILE ]; then
rm -f $FILE
[ $? -eq 0 ] && echo "stop $prog finished."
else
echo "$prog is not running."
fi
}
status() {
if [ -f $FILE ]; then
echo "$prog is running"
else
echo "$prog is stopped."
fi
}

usage() {
echo "Usage: $prog {start|stop|restart|status}"
}

case $1 in
start)
start ;;
stop)
stop ;;
restart)
stop
start ;;
status)
status ;;
*)
usage
exit 1 ;;
esac

  • 函数的返回值分为两类 
    • (1)函数的执行结果返回值 
      • 1:使用echo或printf命令进行输出
      • 2:函数体中调用的命令的执行结果
    • (2)函数的退出状态码 
      • 1:默认取决于函数体中执行的最后一条命令的退出状态码
      • 2:自定义:return (相当于脚本中的exit)
  • 函数可以接受参数在调用函数的时候可以给函数传递参数 
    • 在函数体中,可以使用$1, $2...引用传递给函数的参数,还可以使用$* 或$@调用所有的参数,$#表示传递的参数的个数
    • 在调用函数时候,在函数名后面以空白字符分割给定参数列表即可,例如:functionName arg1 arg2 arg3 arg4...
  • 示例:添加10个用户,添加用户的功能使用函数实现,用户名作为参数传递给函数。这里有一个注意点,就是如果直接return $? ,在函数外面是不能拿到值的。
#!/bin/bash
#
# 5: user exists

addusers() {
if id $1 &> /dev/null; then
return 5
else
useradd $1
retval=$?
return $retval
fi
}

for i in {1..10}; do
addusers ${1}${i}
# 在这里需要注意的是:每次函数的放回结果虽然可以使用$?来引用,但是在后面多次用到的时候,一定要将其使用一个变量保存起来,这里使用一个retval=$?保存了函数执行之后的状态码,以后为了规范起见,所有的函数返回的状态码都使用一个变量将其保存起来,如: variable=$? ,这样就不怕以后引用$?是出现错误。
retval=$?
if [ $retval -eq 0 ]; then
echo "Add user ${1}${i} finished."
elif [ $retval -eq 5 ]; then
echo "user ${1}${i} exists."
else
echo "Unkown Error."
fi
done
  • 综合练习1
写一个脚本:要求
(1)使用函数实现ping一个主机来测试主机的在线状态,主机地址通过参数传递给函数
(2)主程序:测试192.168.7.130 - 192.168.7.139 范围内的各个主机的在线状态
(3)如果主机在线,那么打印黄色的Online字符串,如果主机不在线打印红色的Offline字符串
(4)字符串打印变颜色实现方式,请参照博客:http://yhyblog.cn
#!/bin/bash
# 写一个脚本:要求
# (1)使用函数实现ping一个主机来测试主机的在线状态,主机地址通过参数传递给函数
# (2)主程序:测试192.168.10.1 - 192.168.10.10 范围内的各个主机的在线状态
# (3)如果主机在线,那么打印黄色的Online字符串,如果主机不在线打印红色的Offline字符串
# (4)字符串打印变颜色实现方式,请参照博客:http://yhyblog.cn

if [ $# -lt 2 ];then
echo "请输入两个主机地址作为IP地址的检测范围"
exit 1

fi

START=$1
END=$2


# 检测IP的范围
Ping(){
ping -c1 -w1 192.168.7.13$1 &> /dev/null && echo -e "\E[1;33m 192.168.7.13$1 Online \033[0m" || echo -e "\E[1;31m 192.168.7.13$1 Offline \033[0m"
}

for i in `seq $1 $2`; do
Ping $i
done
  • 综合练习2
写一个脚本 :要求
打印NN乘法表,使用函数实现, 例如:给脚本传递了一个11,那么就是打印11 X 11 的乘法表
#!/bin/bash
# 注意:这里的echo 的 -n选项表示不换行
for((i=1;i<=$1;i++));do
for((j=1;j<=$i;j++));do
echo -n "$j X $i = $[ $j*$i ] "
done
echo
done

(三)bash编程权威指南8

前言:在bash shell编程的脚本里,变量作用域可以分为两种,一种是局部变量,一种是本地变量。
局部变量:作用域是函数的生命周期,在函数结束时被自动销毁。定义局部变量的方法:local 变量=值
本地变量:作用域是运行脚本的shell进程的生命周期,因此,其作用域范围是当前的shell脚本程序文件

  • 示例
#!/bin/bash
#
name=tom

# 记得以后为了避免函数中的变量与本地变量同名,如果同名,会使得具备变量修改本地变量的值,使得本地变量的指针直接指向函数中变量的存储空间。为了避免同名产生的错误,以后在函数中定义的变量都使用局部变量,在变量名前面加上一个local关键字
setname() {
local name=jerry
echo "Function: $name"
}

setname
echo "Shell: $name"

(四)bash编程权威指南9

前言:bash shell中和其他编程语言一样可以使用递归。那什么是递归:程序调用自身技巧称为递归( recursion)

  • 示例:写一个脚本,给脚本传递一个数值,脚本输出这个数值的阶层。例如:5!= 120
#!/bin/bash

func() {
if [ $1 -eq 0 -o $1 -eq 1 ]; then
echo 1
else
echo $[$1*$(func $[$1-1])]
fi
}

func $1
posted @ 2019-07-01 17:32  昌北F4  阅读(89)  评论(0编辑  收藏  举报