SHELL脚本编程基础

一、SHELL脚本基础

1.shell脚本

包含一些命令或声明,并符合一定格式的文本文件

2.执行shell脚本的方法

  • bash **.sh

  • chmod +x **.sh 给脚本加执行权限后使用绝对路径或相对路径运行

  • 给脚本加执行权限后,将脚本所在路径加入$PATH,输入脚本名直接执行

    例如:vim /etc/profile.d/env.sh

           PATH = /data/script/:$PATH
    
  • cat **.sh | bash

3.格式要求

首行shebang机制

在脚本的首行加入#!/bin/bash

4.变量

  • 变量类型:

    • 字符
    • 数值:整形、浮点型
  • bash是动态编译语言,不用事先声明,可随时改类型

  • bash为弱类型语言,运行时会隐式做数据类型转换。无须指定类型,默认为字符型;参与运算会自动进行隐式类型转换,bash不支持浮点数

  • 命名规则:

    1. 不能使用程序中的保留字,如if,for
    2. 只能使用数字、字母及下划线,且不能以数字开头
    3. 见名知义
    4. 统一命名规则:驼峰命名法
    • 建议命名规则:
      1. 变量名大写
      2. 局部变量小写
      3. 函数名小写
      4. 用英文名字,并体现出实际作用
  • 变量种类:

    • 局部变量:生效范围为当前shell进程;对当前shell之外的其他shell进程,包括当前shell的子shell进程均无效

    • 环境变量:生效范围为当前shell进程即其子进程

    • 本地变量:生效范围为当前shell进程中某代码片段,通常指函数

    • 位置变量:$1,$2…..来表示,用于让脚本在脚本代码中调用通过命令行传递给它的参数

      特殊变量:

      • $0 脚本本身的名字

      • $* 所有参数,全部参数合为一个字符串

      • $@ 所有参数,每个参数为独立字符串

      • $# 参数的个数

      • $? 上一条命令退出时的返回值,判断命令是否执行成功

        • 0 为成功
        • 1-255 错误
        • 也可以在脚本中以exit num 指定命令退出时的返回值
        1
        2
        ping -c1 192.168.37.6 &> /dev/null
        echo $?

      set – 清空所有位置变量

  • 局部变量

    • 变量赋值: name= ‘value’

    • 可以使用引用value

      1. 可以是直接字符串: name = “root”

      2. 变量引用:name = “$USER”

      3. 命令引用:name= `COMMAND`

                       name=$(COMMAND)
        
    • 变量引用:${name} 或者 $name

      • “ “ 弱引用,其中的变量引用会被替换为变量值
      • ‘ ‘ 强引用,其中的变量引用不会被替换为变量值,而保持字符串
    • 显示已定义的所有变量: set

    • 删除变量: unset name

  • 环境变量

    • 声明、赋值:

      • export name=VALUE
      • declare -x name=VALUE
    • 变量引用:

      $name,${name}

    • 显示所有环境变量:

      • env
      • printenv
      • export
      • declare -x
    • 删除变量:unset name

    • bash内建的环境变量:

      • PATH
      • SHELL
      • USER
      • UID
      • HOME
      • PWD
      • SHLVL
      • LANG
      • MAIL
      • HOSTNAME
      • HISTSIZE
      • _ 下划线 (上一条命令的最后一条参数)
    • 小括号的妙用:

      只作用于子shell(小括号会开一个子进程)和括号内的命令,而对其父shell及括号外的赋值和命令无影响

      例如:

      1
      2
      3
      4
      5
      6
      TITLE=ceo
      (echo $TITLE;TITLE=coo;echo $TITLE)
      ceo
      coo
      ehco $TITLE
      ceo

    5.算术运算

    5.1bash中的算术运算:

    +,-,*,/,%取余,**乘方,乘法符号在有些场景中需要转义

    实现算术运算:

    1. let var=算术表达式

    2. var=$[算术表达式]

    3. var=$((算术表达式))

    4. var=$(expr arg1 arg2 arg3 …)

      此时相当于调用expr命令,算术表达式中的每个元素相当于expr命令的参数,所以必须都要有空格,而且*需要转义

      1
      2
      3
      [root@centos8 ]#a=$(expr 2 \* 3)
      [root@centos8 ]#echo $a
      6
    5. declare -i var= 数值 (即将var声明为整数)

    6. echo ‘算术表达式’ | bc

    5.2bash有内建的随机数生成器变量:$RANDOM(0-32767)

    例如:生成0-49之间随机数

    echo $[$RANDOM%50](即对50作取余运算)

    5.3短路与 短路或
    • 短路与:cmd1 && cmd2 :

      • 如果cmd1执行成功,执行cmd2
      • 如果cmd1执行失败,不再执行cmd2
    • 短路或:cmd1 || cmd2:

      • 如果cmd1执行成功,不再执行cmd2

      • 如果cmd1执行失败,执行cmd2

         示例:
        
1
2
3
4
[root@centos8 ~]#x=hello
[root@centos8 ~]#y=ok
[root@centos8 ~]#test $x = $y && echo equal || echo not equal
not equal

上述示例的说明:

  • test为条件测试命令

  • $x = $y等号两侧必须有空格,否则就成了赋值

  • cmd1 && cmd2 || cmd3的逻辑为:如果cmd1成功,则执行cmd2;

    如果cmd1不成功,不用执行cmd2,但这时cmd1 && cmd2也就整体不成功,就需要执行cmd3

  • test等价于[ ],注意中括号和里面的表达式之间要有空格

  • [[ ]] 双中括号可以在表达式中使用正则表达式,且为扩展的正则表达式

    此时要使用 =~ (表示包含的意思) ,即左侧的字符串是否被右侧的pattern所匹配,例如:

    1
    2
    #str=gooood;[[ $str =~ o{2,} ]] && echo true
    true
    1
    2
    3
    4
    5
    6
    #判断n是否为数字
    #n=123;[[ $n =~ ^[[:digit:]]+$ ]] && echo true
    true
    #判断是否为.sh为后缀的文件
    #FILE=f.sh;[[ $FILE =~ \.sh$ ]] && echo true
    true
5.3数字的比较
  • -eq 等于
  • -ne 不等于
  • -lt 小于
  • -le 小于等于
  • -gt 大于
  • -ge 大于等于
5.4判断文件
  • -a 或者 -e, 判断文件是否存在,例如:

    1
    FILE=/data/test.txt;[ -a "$FILE" ] || touch $FILE
  • -x 是否可执行

  • -r 是否可读

  • -w 是否可写

    示例:是否可读并且可写

    1
    [ -r "$FILE" -a -w "$FILE"]

6 read命令

读入标准输入赋值给变量(注意变量不要加$)

  • -p 指定要显示的提示

  • -s 静默输入,一般用于密码

  • -n N 指定输入的字符长度N

  • -d ‘字符’ 输入结束符

  • -t N timeout为N秒

    例如:

1
2
3
echo -e "please input your name:\c"
read name
echo your name is $name

上例用echo -e 以及\c 目的是不要产生换行,更符合习惯

其实,read本身就有-p选项,可以实现提示功能:

1
2
read -p  "please input your name:" name 
echo your name is $name

实现鸡兔同笼问题小脚本:

1
2
3
4
5
6
read -p "请输入头的数量:" head
read -p "请输入脚的数量:" foot #让鸡和兔都抬起一半的脚,再各抬起一只脚,此时剩余的脚就是兔子的数量
rab_num=$[foot/2-head]
chick_num=$[head-rab_num]
echo "鸡的数量是$chick_num;兔的数量是$rab_num"

7.条件判断

7.1单分支
1
2
3
if 判断条件;then
cmd
fi
7.2双分支
1
2
3
4
5
if 判断条件;then
cmd1
else
cmd2
fi
7.3多分支
1
2
3
4
5
6
7
8
9
if 判断条件;then
cmd1
elif 判断条件;then
cmd2
elif 判断条件;then
cmd3
else
cmd4
fi

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
read -p "input your age:" AGE
if [[ ! $AGE =~ ^[0-9]+$ ]];then
echo "you need input digit"
exit
elif [ $AGE -lt 18 ];then
echo "good good study"
elif [ $AGE -le 60 ];then
echo "good good work"
elif [ $AGE -le 120 ];then
echo "enjoy life"
else
echo "welcom to the earth"
fi
7.4 case语句
1
2
3
4
5
6
7
8
9
10
11
12
13
case 变量引用 in
PAT1)
cmd1
;;
PAT1)
cmd2
;;
PAT3)
cmd3
;;
*)
cmd4
esac

示例,判断输入的是yes还是no

1
2
3
4
5
6
7
8
9
10
11
read -p "请输入是否:" INPUT
case $INPUT in
[Yy]|[Yy][Ee][Ss])
echo "你输入的是yes"
;;
[Nn]|[Nn][Oo])
echo "你输入的是no"
;;
*)
echo "你输入错误"
esac

上例也可以简化如下:

1
2
3
4
5
6
7
8
9
10
11
12
read -p "请输入yes或no: " INPUT
INPUT=`echo $INPUT | tr "A-Z" "a-z"`
case $INPUT in
y|yes)
echo "你输入的是yes"
;;
n|no)
echo "你输入的是no"
;;
*)
echo "你输错了,请输入yes或no"
esac

8.bash如何展开命令行

  • 把命令行分成单个命令词
  • 展开别名
  • 展开大括号的声明{}
  • 展开波浪符的声明 ~
  • 命令替换$() 和` `
  • 再次把命令行分成命令词
  • 展开文件通配符(*、?、[abc]等)
  • 准备I/O重定向
  • 运行命令

9.bash的配置文件

按生效范围划分,存在两类:

  • 全局配置

    • /etc/profile
    • /etc/profile.d/*.sh
    • /etc/bashrc
  • 个人配置

    • ~/.bash_profile
    • ~/.bashrc

shell登录的两种方式:

  • 交互式登录

    1. 直接通过终端输入账号密码登录
    2. 使用su - username 切换的用户
    3. 执行顺序:/etc/profile –> /etc/profile.d/*.sh –> ~/.bash_profile–>~/.bashrc–>/etc/bashrc
  • 非交互式登录

    1. su username
    2. 图形界面下打开终端
    3. 执行脚本
    4. 任何其他的bash实例
    5. 执行顺序:/etc/profile.d/*.sh –> /etc/bashrc –> ~/.bashrc
  • profile类:为交互式登录的shell提供配置

    • 全局:/etc/profile /etc/profile.d/*.sh
    • 个人: ~/.bash_profile
    • 功能:用于定义环境变量、运行命令或脚本
  • bashrc类:为非交互式和交互式登录的shell提供配置

    • 全局:/etc/bashrc
    • 个人:~/.bashrc
    • 功能:用于定义别名和函数、定义本地变量

10.set命令

  • $- 变量(可以用echo $-查看)

    • h:hashhall,打开这个选项后,shell会将命令所在路径hash下来,避免每次都要查询.set +h将选项关闭,set -h 开启
    • i:包含这个选项说明当前的shell是个交互式shell。在脚本中,i选项是关闭的
    • m:打开监控模式,就可以通过Job control来控制进程的停止、继续,后台或者前台执行
    • B:大括号扩展。比如启用echo {a..z}
    • H:该选项打开,可以通过!感叹号来完成历史命令调用,例如!!返回上一条命令,!n 返回第n条历史命令

    在工作中强烈建议在脚本中加入以下命令:

  • set -u : 在扩展一个没有设置的变量时,显示错误信息,等同于set -o nounset.在脚本中使用该命令可以避免安全风险,比如:

    1
    2
    3
    set -u
    Dir=/data
    rm -rf $DIR/*

    上述脚本假设没有开启set -u , DIR是个不存在的变量,会被解析成空,将导致直接将根目录下的所有文件删除

  • set -e : 如果某个命令执行错误,直接整个脚本退出