bash脚本编程总结

bash脚本编程总结

shell简介

GNU/Linux shell 是一个特殊的交互式实用程序。它为用户提供了一种启动程序、管理文件系统中的文件和管理运行在Linux系统上的进程的方式。shell的核心是命令提示符。命令提示符是shell用于交互的组成部分。它允许输入文件命令,解释命令,然后在内核中执行命令。

 查看当前系统中有哪些shell:

# cat /etc/shells 
/bin/sh
/bin/bash
/sbin/nologin
/bin/dash
/bin/tcsh
/bin/csh
/bin/zsh

shell脚本:

shell script 程序化脚本,针对shell所写的脚本,将许多指令汇合在一起,去处理复杂的动作。

 编写规范:

    第一行必须顶格,

       #!/bin/bash   ## 用来声明此脚本所使用的shell语法

       其他程序 #!/usr/bin/python

 除每一行的#用来声明使用的shell,其它的以#开头的行均为注释,会被解释器忽略;

 shell脚本应该的基本注释信息

1、scripts的功能

2、scripts的版本信息

3、scripts的作者与联络方式

4、scripts的版权宣告方式

5、scripts内的特殊指令,使用绝对路径来调用

6、scripts运作时需要的环境变量预先宣告预设定

7、scripts应该包含详细的注释信息,以便于查阅

 

shell文件的执行及排错

执行shell文件:

1、相对路径执行脚本,先给予shell 脚本执行权限,然后切换到脚本所在的目录下,直接执行脚本

    # cd /home/scripts/ ;chmod +x test.sh ; ./test.sh

2、绝对路径执行  

    # chmod +x /home/scripts/test.sh; /home/scripts/test.sh

3、以bash程序来执行,使用bash或sh来执行脚本,此时执行脚本不需要提前给予权限

    # sh test.sh ; bash test.sh

脚本执行毁在当前shell下开启一个子shell,然后在子shell中执行该脚本,当脚本执行完之后再关闭相应的子shell

 shell文件的基本排错:

    1、使用vim 编辑脚本的时候,如果有基本的语法错误会用特殊颜色显示。

2、bash -n  scriptsname.sh  :不执行脚本,只返回语法错误信息

3、bash -x scriptsname.sh :追踪执行过程  如果有错误就不在往下执行,直接打印出错误。

 

过程式编程语言:

    顺序执行:从上往下依次执行语句

    选择执行:测试条件,可能会多个测试条件,某条件满足时,则执行对应的分支

    循环执行:将同一段代码反复执行多次;因此,循环必须有退出条件;否则,则陷入死循环;

  

变量类别:

    本地变量

    环境变量

       export:导出

    局部变量

    位置变量

       $1, ..., $n, ${10}

       位置变量偏移:shift[n]

    特殊变量:

       $?:状态返回值

       $#: 传递给脚本参数的个数

       $*:引用传递给脚本的所有参数

       $@:引用传递给脚本的所有参数

       $0: 命令本身

  

自定义shell进程的状态返回值:  exit [n]

bash编程之交互编程   read

# read -p "Plz input a username: " userName

显示菜单:

cat << EOF
-------menu------------
cpu) show cpu infomation
mem) show memory infomation
*) quit
-------menu------------
EOF


bash的条件判断式语句:

 单分支的if语句:

    if 测试条件; then

       选择分支

    fi

    表示条件测试状态返回值为值,则执行选择分支;

 

测试条件:在bash中是命令 (test EXPR, [ EXPR ] ) 或由 [[ EXPR ]]

在bash运行至if时,其后的命令会被执行,其状态结果则作为判断标准:

       0:表示真

       1-255:表示假

     如果条件包含比较之意,则必须使用

示例:

输入一个用户名,如果用户不在在,则创建之。
#/bin/bash
read -p "Plz input a username: " userName
if ! id $username &> /dev/null; then
    useradd $username
fi

 

条件测试:

命令执行成功与否即为条件测试

    test EXPR

    [ EXPR ]

    [[ EXPR ]]

试类型:根据比较时的操作数的类型

    整型测试:整数比较

    字符测试:字符串比较

    文件测试:判断文件的存在性及属性等

     注意:比较运算通常只在同一种类型间进行

 

整型测试:

-gt: 大于,例如 [$num1 -gt $num2 ]
-lt: 小于
-ge: 大于等于
-le: 小于等于
-eq: 等于
-ne: 不等于


字符串测试:

    双目:

        >: [["$str1" > "$str2" ]]

        <:

       >=

       <=

        ==

        !=

     单目:

       -n String: 是否不空,不空则为真,空则为假

       -z String: 是否为空,空则为真,不空则假

文件测试:

-a FILE: 文件是否存在。
-e FILE: 存在则为真;否则则为假;
 
-f FILE: 存在并且为普通文件,则为真;否则为假;
-d FILE: 存在并且为目录文件,则为真;否则为假;
-L/-h FILE: 存在并且为符号链接文件,则为真;否则为假;
-b: 块设备
-c: 字符设备
-S: 套接字文件
-p: 命名管道
 
-s FILE: 存在并且为非空文件则为值,否则为假;
 
-r FILE:文件是否具有可读权限
-w FILE:文件是否有写权限
-x FILE:文件是否有可执行权限

    字串测试中的模式匹配

    [[ "$var" =~ PATTERN ]]

 

双分支的if语句:

两个分支仅执行其中之一。

注意:如果把命令执行成功与否当作条件,则if语句后必须只跟命令本身,而不能引用。

语法格式:

if 测试条件; then
    选择分支1
else
    选择分支2
fi

示例:

通过命令行给一个文件路径,如果此文件中存在空白行,则显示空白行的总数;否则,显示无空白行;

bash scripts.sh /etc/issue
#/bin/bash
if grep "^[[:space]]*$" $1 &> /dev/null; then
    echo "$1 has $(grep "^[[:space]]*$" $1 | wc -l) blank lines."
else
    echo "No blank lines"
fi

 

 多分支的if语句:

    多个分支只执行满足某一个条件的语句

语法格式:

if 条件1; then
    分支1
elif 条件2; then
    分支2
elif 条件3; then
    分支3
...
else
    分支n
fi

示例:

传递一个用户名给脚本:1、如果此用户的id号为0,则显示说这是管理员;
2、如果此用户的id号大于等于500,则显示说这是普通用户;3、否则,则说这是系统用户;
#!/bin/bash
#
if [ $# -lt 1 ]; then
    echo "Usage: `basename $0` username"
    exit 1
fi
if ! id -u $1 &> /dev/null; then
    echo "Usage: `basename $0` username"
    echo "No this user $1."
    exit 2
fi
if [ $(id -u $1) -eq 0 ]; then
    echo "Admin"
elif [ $(id -u $1) -ge 500 ]; then
    echo "Common user."
else
    echo "System user."
fi

 

case语句:

有多个测试条件时,case语句会使得语法结构更明晰

语法格式:

case 变量引用 in
PATTERN1)
    分支1
    ;;
PATTERN2)
    分支2
    ;;
    ...
*)
分支n
    ;;
esac

    PATTERN:类同于文件名通配机制,但支持使用|表示或者;

       a|b: a或者b

       *:匹配任意长度的任意字符

       ?: 匹配任意单个字符

       []: 指定范围内的任意单个字符

 示例

写一个脚本,对/etc/目录及内部的所有文件打包压缩
1、显示一个菜单,让用选择使用的压缩工具:
xz) xz compress tool
gz) gzip compress tool
bz2) bzip2 compress tool
2、根据用户选择的工具,对/etc执行相应的操作并保存至/backups目录,文件形如/backups/etc-日期时间.tar.压缩后缀
#!/bin/bash
#
[ -d /backups ] || mkdir /backups
cat << EOF
xz) xz compress tool
gz) gzip compress tool
bz2) bzip2 compress tool
EOF
read -p "Plz choose an option: " choice
case $choice in
xz)
    tar -Jcf /backups/etc-`date +%F-%H-%M-%S`.tar.xz /etc/*
    ;;
gz)
    tar -zcf /backups/etc-`date +%F-%H-%M-%S`.tar.gz /etc/*
    ;;
bz2)
    tar -jcf /backups/etc-`date +%F-%H-%M-%S`.tar.bz2 /etc/*
    ;;
*)
    echo "wrong option."
    exit 1
    ;;
esac


bash的循环语句:

for 循环语句 

    遍历有限的元素列表

格式一:

for变量名 in LIST;do
    循环体
done

 格式二、

for ((初始条件;测试条件;修改表达式)); do
    循环体
done

    # LIST:列表,中间包括一个或多个元素

# 退出条件:遍历列表结束

# 循环体:依赖于调用变量来实现其变化;

 

生成列表的方式:

    1、手动给个列表:

       for i in 1 2 3 4 5;

    2、数值列表:

       {start..end}

       `seq [start [increment]] end`   #命令引用

    3、$*, $@

    4、命令生成列表

示例:

求100以内所有正整数之和:(for,while,until实现对比)
=============================
for格式一实现
#!/bin/bash
declare -i sum=0
for i in {1..100}; do
  sum=$[$sum+$i]
done
echo $sum
----------------------------------------------
for格式二实现
declare -i sum=0
 
for ((counter=1;$counter<= 100; counter++)); do
    let sum+=$counter
done
echo $sum

===================================
until循环实现
#!/bin/bash
#
declare -i count=1
declare -i sum=0

until [ $count -gt 100 ]; do
    let sum+=$count
    let count++
done

echo $sum

====================================
while循环实现
#!/bin/bash
#
declare -i count=1
declare -i sum=0

while [ $count -le 100 ]; do
    let sum+=$count
    let count++
done

echo $sum

 

while循环:

    while适用于循环次数未知,或不便用for直接生成较大的列表时;

    语法格式:

while 测试条件; do
    循环体
done

    # 测试条件为真,进入循环;测试条件为假,退出循环;

    # 测试条件一般通过变量来描述,需要在循环体不变量地改变变量的值,以确保某一时刻测试条件为假,进而结束循环;

示例:

提示用户输入一个用户名,如果用户存在,就显示用户的ID号和shell;否则显示用户不存在;
显示完成之后不退出,再次重复前面的操作,直到用户输入q或quit为止;
read -p "Plz enter a username: " userName
while [ "$userName" != 'q' -a "$userName" != 'quit' ]; do
    if id $userName &> /dev/null; then
grep "^$userName\>" /etc/passwd | cut -d: -f3,7
    else
echo "No such user."
    fi
  read -p "Plz enter a username again: " userName
done

   while循环:遍历文本文件

   语法格式:

while read 变量名; do
     循环体
done < /path/to/somefile

   注意:

       变量名,每循环一次,记忆了文件中一行文本

       同时也可以使用管道

    示例:

显示其ID号为偶数的用户的用户名、ID号和SHELL
#!/bin/bash
while read line; do
   userID=`echo $line | cut -d: -f3`
   if [ $[$userID%2] -eq 0 ];then
       echo $line | cut -d: -f1,3,7
   fi
done < /etc/passwd

 

until循环

    未知次数的循环,由测试条件决定是否退出循环;

    until语句和while语句正好相反,while语句判断条件为真时,进行循环操作;

    而until则是判断条件为假时,进行循环操作

   语法格式:

until 测试条件; do
    循环体
done

    # 如果测试结果为“假”,则进入循环;退出条件为,测试条件为真;

    # 测试条件一般通过变量来描述,需要在循环体不变量地改变变量的值,以确保某一时刻测试条件为真,进而结束循环;

 示例:

查看用户登录
#!/bin/bash
 read -p "Plz enter a username: " userName
until who | grep "\<$userName\>" &> /dev/null; do
  sleep 5
  echo "not here"
done
echo "here"
============================================================
while 循环实现
#!/bin/bash
#
read -p "Plz enter a username: " userName
while true; do
    if who | grep "\<$userName\>" &> /dev/null; then
        break
    fi
    echo "not here."
    sleep 5
done
echo "$userName is logged on."


  循环控制:

    continue: 提前进入下一轮循环

        用于条件语句中,仅在某些个特殊场景提前进入;

    break [n]:跳出当前循环

       用于条件语句中,

  

bash的函数:

如果你要编写大型的Shell脚本程序,那么模块化必须的,定义函数来模块化脚本程序是个不错的选择。

# 可调用:使用函数名,函数定义必须在所有调用之前

    # 函数名出现的地方,会被自动替换为函数;

   语法格式一:

    function 函数名 {

       函数体

    }

   语法格式二:

    函数名( ) {

       函数体

    }

    函数的返回值:

        函数的执行结果返回值:代码的输出

             函数中的打印语句:echo,print

        函数中调用的系统命令执行后返回的结果

    执行状态返回值:

        函数体中最后一次执行的命令状态结果(查看:echo$?)

        自定函数执行状态的返回值:return #

    函数可以接受参数:

    在函数体中调用函数参数的方式同脚本中调用脚本参数的方式:位置参数

           $1, $2, ...

           $#, $*, $@

示例:

编写服务脚本script.sh {start|stop|restart|status}

    1) start: 创建/var/lock/subsys/script.sh

    2) stop: 删除此文件

    3) restart: 先删除文件,再创建文件

    4) status: 如文件存在,显示running,否则,显示stopped

#!/bin/bash
#
# chkconfig: 2345 67 34
#
srvName=$(basename $0)
lockFile=/var/lock/subsys/$srvName
start() {
    if [ -f $lockFile ];then
echo "$srvName is already running."
return 1
    else
touch $lockFile
[ $? -eq 0 ] && echo "Starting $srvName OK."
return 0
     fi
}
stop() {
    if [ -f $lockFile ];then
rm -f $lockFile &> /dev/null
[ $? -eq 0 ] && echo "Stop $srvName OK" && return 0 
    else
echo "$srvName is not started."
return 1
    fi
}
status() {
    if [ -f $lockFile ]; then
echo "$srvName is running."
    else
echo "$srvName is stopped."
    fi
    return 0
}
usage() {
     echo "Usage: $srvName {start|stop|restart|status}"
     return 0
}
## 程序主体
case $1 in
start)
    start
    ;;
stop)
    stop ;;
restart)
    stop
    start ;;
status)
    status ;;
*)
    usage
    exit 1 ;;
esac

 

bash的数组:

    是一种数据结构,有相关的数据项组成,每个数据项,称为数组的元素,且可采用索引的方式取得个元素的值。

   数组:

    数组名+索引

       数组元素

    索引的表示方式:

       数字索引:a[index]

           a[0], a[1] 

    bash 4.0的关联数组

       a[hello], a[hi]

       declare -a

       declare -A

支持稀疏格式:

       仅一维数组

 

   数组的赋值:

       一次对一个元素赋值:单个赋值,以index作为索引号,索引号从0开始

           a[0]=$RANDOM    a[1]=345

        一次对全部元素赋值:使用小括号为数组赋值默认以空格分隔

           a=(red blue yellow green)

        按索引进行赋值:

           a=([0]=green [3]=red [2]=blue [6]=yellow)

        命令替换:

        用户输入:

           read -a ARRAY 

    数组的访问: 

       用索引访问:

           ARRAY[index] 

    数组的长度:

       ${#ARRAY[*]}

       ${#ARRAY[@]} 

    从数组中挑选某元素:

       ${ARRAY[@]:offset:number}

           切片:

              offset: 偏移的元素个数

              number: 取出的元素的个数

       ${ARRAY[@]:offset}:取出偏移量后的所有元素 

       ${ARRAY[@]}: 取出所有元素

     数组复制:

        要使用${ARRAY[@]}

        $@: 每个参数是一个独立的串

        $*: 所有参数是一个串

示例:复制一个数组中下标为偶数的元素至一个新数组中

#!/bin/bash
declare -a mylogs
logs=(/var/log/*.log)
    echo ${logs[@]}
for i in `seq 0 ${#logs[@]}`; do
     if [ $[$i%2] -eq0 ];then
         index=${#mylogs[@]}
         mylogs[$index]=${logs[$i]}
     fi
done
    echo ${mylogs[@]}
生成10个随机数,升序排序
#!/bin/bash
for((i=0;i<10;i++));do
    rnd[$i]=$RANDOM
done
 echo -e "total=${#rnd[@]}\n${rnd[@]}\nBegin to sort"
for((i=9;i>=1;i--));do
    for((j=0;j<i;j++));do
        if [ ${rnd[$j]} -gt ${rnd[$[$j+1]]} ] ;then
            swapValue=${rnd[$j]}
            rnd[$j]=${rnd[$[$j+1]]}
            rnd[$[$j+1]]=$swapValue
        fi
    done
done
  echo ${rnd[@]}

 

 

 


你可能感兴趣的:(shell,bash)