shell 脚本知识点

shell 脚本

shell 语言特点

  • 属于弱类型语言:所以定义变量时不需要指定变量类型
  • 属于解释型语言:编写好的shell脚本需要指定的shell解释器才能执行;
  • 仅一种变量类型:所有数据实际上都是以字符串形式处理,所有变量和数据都可以看作是字符串;

shell 变量相关

1、变量定义规则

  • 定义shell变量时,如果没有赋值,则这个变量值为空。
  • 变量名由数字、字母和下划线组成,且不允许使用数字开头,一般采用蛇形命名法。

例如:

# 有空格,不合法
AGE = 18

# 存在非数字、字母和下划线字符,不合法
chunk-name=fdfasfas.js

# 数字开头,不合法
8Name=tom

2、三种变量类型

  • 普通变量:只对当前 Shell 或脚本有效,不会自动被子进程继承。
  • 全局变量:使用 export 申明的变量,又叫作环境变量,能被当前Shell创建的子进程继承。
  • 局部变量:在函数内部使用 local 申明的变量,只在函数内部生效。

例如:

# 普通变量,当前 Shell 或脚本有效
age=18

# 全局变量,,能被当前Shell创建的子进程继承。
export age=18

# 局部变量,函数内生效
get_name()
{
    local name=tom
}

3、变量赋值规则

  • 空格严格:变量赋值时 = 号两边不能存在任何空格,否则报错
  • 引号保护:通过赋值符号( = )进行赋值时,如果变量值存在空格或其他特殊字符,需要使用引号保护,表示是一个整体。

例如:

NAME=TOM

# 存在空格,需要使用引号让其成为整体
NAME="John Wilson"

# 存在特殊字符,需要转义或用单引号
PRICE='$18'

4、变量展开规则

4.1 引用变量规则

​通过 反引号 `` 或 $ 符号可以对变量进行引用,如果变量后面紧跟着其他字母或数字,需要使用花括号 { } 可以明确界定变量名。否则shell 可能会将其误认为是变量名的一部分。

例如

file="test"

# 通过大括号来界定变量名
echo "${file}.txt" 
4.2 变量展开规则
  • 不加双引号保护时:因为变量展开时,原始值中可能包含换行符等特殊字符,shell会根据 IFS (空格、换行符、Tab) 分隔变量为多个单词。如果还含有其他一些特殊字符,例如通配符,则展开之后的结果可能还会被进一步处理。
  • 加了双引号保护时:展开后的内容原样输出,不再做分词处理。

例如:变量不加引号的情况

file=$(ls -1d /home/ehigh/test/*)
echo $file

# 输出结果
/home/ehigh/test/1 /home/ehigh/test/2 /home/ehigh/test/3

echo $filename

  1. 先将变量展开,替换为具体的变量值,因为ls命令的输出是多行内容,所以原始值中包含换行符。
  2. shell会根据 IFS(空格、换行符、Tab) 将展开后的变量分隔为为多个单词
  3. shell 会将分隔后的单词作为参数传给 echo,echo命令再逐个以空格间隔打印这些单词

例如:变量加双引号的情况

file=$(ls -1d /home/ehigh/test/*)
echo "$file"

# 输出结果
/home/ehigh/test/1
/home/ehigh/test/2
/home/ehigh/test/3

echo "$file"

  1. 先将变量展开,替换为具体的变量值,因为ls命令的输出是多行内容,所以原始值中包含换行符。
  2. 因为存在双引号,所以shell不会根据IFS来对变量值中的换行符、空格进行分词操作
  3. shell 会将整个字符串传输给echo,所以echo就实现了原样输出。

5、变量销毁方式

  • 自动销毁:脚本执行完毕后,运行该脚本的 Shell 进程也会结束,所有在该进程中定义的变量都会自动释放,不会再保留在系统中。

  • 手动销毁:手动使用 unset 删除变量。在交互式 Shell 中手动定义的变量会一直存在,此时就需要手动销毁。

例如:销毁变量

name=tom

# 销毁变量
unset name

shell 内置变量

1、特殊变量( _ )

  • _变量:变量名为 _ 时,表示“不关心这个变量的值”。如果用户有多个输入,就可以可以利用这一约定来丢弃不需要的输入字段。

2、位置变量

位置变量是shell内置的一些变量,不用定义可以直接使用$来进行引用,常见的位置变量有这些:

  • $0:表示的是当前脚本的名称
  • $n:n大于1,传递給脚本的第n个参数
  • $#:一共有多少个参数传递给了脚本
  • $@: 传递给脚本的所有参数
  • $$:当前shell脚本的PID
  • $?:上一个命令的退出状态。如果命令执行成功,则退出状态为 0

例如:获取当前脚本所在的路径

current_path=$(cd $(dirname $0); pwd)

3、环境变量

3.1 查看当前 Shell 环境中的环境变量

环境变量是定义在当前环境(进程)中、并可以被当前进程及其所有子进程访问和继承的特殊变量。

操作系统
   └── Shell进程(当前)  ← env查看的是这一层
        └── 子进程(脚本、程序)← 继承上面环境变量

例如:查看当前shell进程的环境变量

# 直接执行env命令即可
env
3.2 和用户相关的环境变量
  • SHELL:当前用户使用的默认shell路径。例如:SHELL=/bin/bash
  • USER :当前登录的用户名。例如:USER=root、
  • HOME:当前用户的家目录路径。例如:HOME=/root
  • EDITOR:打开某个文件时,默认文本编辑器
3.3 和系统配置相关的环境变量
  • PATH:指定以相对路径执行某个可执行程序的搜索路径,多个路径以:分隔,并且搜索时不会递归查找,只会查找所指定路径下是否存在该可执行文件。
  • LANG :指定系统使用的语言环境。格式为:“语言代码_国家或地区.字符集”,LANG变量常用的两种语言环境。中文环境:zh_CN.UTF-8,英文环境:en_US.UTF-8
  • IFS:内部字段分隔符(Internal Field Separator)的缩写,定义Shell在进行分词或字段拆分时使用的字符。当shell在处理命令或变量展开时,会依据IFS中的字符来确定如何将一个字符串拆分为多个字段。IFS 默认指定的分隔符是空格、制表符(tab)和换行符的组合。

例如:设置PATH变量

export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin

例如:设置当前环境为应为英文环境

export LANG=en_US.UTF-8

例如:查看当前IFS的值

printf "%q\n" "$IFS"

# 输出信息
    # 空格表示空格字符
    # `\t`表示Tab字符
    # `\n`表示换行字符

shell 字符相关

1、会被 shell 解释的特殊字符

1.1 通配符类
  • *:表示匹配任意多个字符

  • :表示匹配单个任意字符

  • []:表示匹配范围内的单个字符

1.2 重定向符和管道符
  • > :输出重定向,覆盖原文件。

  • >> :追加输出重定向,内容追加到文件尾部。

  • < :输入重定向。

  • | :管道符,连接两个命令,左边命令的输出作为右边命令的输入。

1.3 命令控制符
  • ; :在一行内执行多个命令时,分隔多个命令,让多个命令顺序执行,如 ls; pwd

  • & :命令后台执行,如 sleep 60 &

  • && :前面的命令执行成功后再执行后面的命令。

  • || :前面的命令执行失败时再执行后面的命令。

1.4 变量与命令控制换类
  • $ :变量引用或命令替换,如 $HOME$(ls)
  • command$() :命令替换,执行命令后替换为结果。
  • ${} :变量扩展,如 ${var}
  • ~ :代表用户家目录路径。
  • # :用于在Shell中表示注释,后面文字不被执行。
1.5 引号和转义符
  • ' (单引号):使引号内所有内容保持字面量,不解析变量。

  • " (双引号):解析变量,但不解析大部分特殊字符。

  • \ :转义符,去除特殊字符本身的特殊含义。

2、三种引号的作用

​ 单引号和双引号的作用是在字符串中存在空格或会被shell解释的特殊字符时,确保字符串能被做为一个整体进行处理,避免shell根据IFS来拆分这个字符串。但是单双引号在处理特殊字符时有所不同。

2.1 单引号的特点

单引号中的任何内容都会原样输出,不会被shell进行解释,但是单引号保护的字符串中不能存在单引号,因为单引号中的内容不会被shell解释,所以加了转义字符也没用。

2.2 双引号的特点

双引号中存在一些特殊字符仍然会被shell解释

  • 美元符:shell 会将美元符号( $) 解释为变量引用符号,美元符号后面的字符串会被当作变量进行变量引用。
  • 反引号:反引号(``)效果等同于 $ 字符,也会被shell解释为变量的引用符。
  • 反斜杠:反斜杠(\)会被shell解释为转义字符,例如:“\n” 会被解释为换行符。
  • 双引号:shell在遇到双引号 (") 时认为这是字符串的开始或结束,如果双引号中的字符串包含双引号,则shell无法确定哪一个是字符串的结束,所以这种情况需要对字符串中的双引号进行转义。

例如:双引号中存在双引号需要转义

echo "\"Hello World\""

例如:单引号和双引号嵌套

NAME=TOM

# My Name is 'TOM'
echo "My Name is '${NAME}'"

说明:上面说了单引号中的任何内容都会原样输出,但是这里的单引号在双引号内,双引号内的单引号就是一个普通字符,双引号内的$会被shell解释,所以能展开变量。

2.3 反引号的特点

​ 反引号的作用和$一样,是变量引用的作用。反引号内的字符当作一个命令执行,将commnd替换为命令的输出。

3、shell 三种括号的作用

3.1 大括号的作用
3.1.1 单个大括号场景

命令块(匿名函数):用大括号 { } 将多条命令组合在一起时,这些命令会在当前 Shell 中依次执行,整个命令块的退出状态码就是最后一条命令的退出状态码。

格式{ command1; command2; }

  • 要求括号两边必须有空格

  • 每个指令都需要以分号;结尾,包括最后一个指令。

例如


花括号扩展:用于生成一组字符串。将若干字符串用逗号分隔放在大括号中,Shell 会将其扩展为多个独立的字符串。

  • 格式{ str1,str2,str3 }

例如

echo {a,b,c,d,e}.txt

# a.txt b.txt c.txt d.txt e.txt

范围扩展:用于生成有序的数字或字母序列。通过指定起始和结束值,Shell 会生成从起始值到结束值的序列。

  • 格式{ start..end }

例如:删除指定后缀的日志文件

sudo rm -f  mysql-bin.0018{37..79}
3.1.2 $ 和 { } 组合
  • 作用:变量定界符,表示变量的起始位置。 防止shell将变量后面紧跟着其他字母或数字认为是变量的一部分。

  • 格式${command}

3.2 中括号的作用
3.2.1 单个中括号
  • 作用一:是test 命令的别名。用于实现条件测试,返回值是命令的退出状态码
  • 作用二:是shell 的通配符。匹配特定的字符集或字符范围
3.2.2 $ 和 [] 组合
  • 实现算术扩展,执行基本的算数运算,参数是一个不带赋值符的表达式,但是已经被$(( )) 所取代
3.3 小括号的作用
3.3.1 单个小括号场景
  • 实现命令组合。在当前进程下开启一个子进程来执行括号内的指令,当前进程的环境信息会被子进程继承,但是子进程的变量和更改不会影响当前进程,整个的退出状态码是最后一个命令的退出状态码。

  • 格式( cd /some/dir && ls ) (如果存在多个命令命令之间如果在同一行通常需要用分号,最后一个命令的分号可选。)

例如:开启一个子进程在后台执行命令组合。

( ping -c1 $ip3 &> /dev/null
[ $? -eq 0 ] && echo "$ip3 is up" || echo "$ip3 is down" 
) &
3.3.2 两个小括号嵌套:
  • 是shell中的一个内置命令,一般是用来进行整数的逻辑判断的。例如比较两个整数的大小等。

例如

if (( 3 > 2 )) ;then
    echo "3 > 2"
fi
3.3.3 < 和 ( ) 组合使用:
  • 当 ( ) 和 < 组合时,叫作进程替换。这种语法会将小括号里面命令的输出存储到一个文件描述符(通常是一个命名管道或临时文件)中,然后其他命令可以像读取普通文件一样读取命令的输出。
  • 格式:<(command) — 可以假想这就是一个匿名文件,某些命令可以像读取普通文件一样读取该文件的内容。

例如

# 相当于cat 直接读取 ‘<(ls)’ 这个匿名文件做为其标准输入
cat <(ls)

shell Heredoc 语法

1、Heredoc 的作用

​ Heredoc 是 Here Document 的缩写,是shell中的一种特殊的语法结构,可以理解为一种特殊的输入重定向。用来将一个多行的字符串直接重定向到某个命令的标准输入中,而不需要通过文件或管道等中间步骤。

  • 语法格式command <<DELIMITER
    • command:需要能接收标准输入的命令,例如cat等。
    • DELIMITER:是固定搭配,<< 后必须跟一个定界符。定界符可以随意定义,一般使用EOF表示结束

例如

cat > /etc/rsync.pas <<EOF
rsync:123456
EOF
  • <<EOF:会将指导遇到定界符EOF的多行内容做为某个命令的标准输入。
  • cat > /etc/rsync.pas:通过Heredoc 获取到的标准输入,然后重定向到指定文件

2、Heredoc 定界符

定界符可以随意定义,一般使用EOF表示结束,但是定界符是否用引号括起来,对文件的处理有不同。

  • 定界符无引号:输入的多行字符串中,如果包含一些特殊字符或变量引用,会被shell解释后再重定向到指定命令的标准输入中。

  • 定界符有引号:使用双引号或单引号将定界符括起来后,会将内容视为纯文本,不会进行任何拓展。

例如

# 变量会被展开,然后在做为cat的标准输入
cat > /etc/rsync.pas <<EOF
rsync:$passwd
EOF

# 所有内容都内容视为纯文本,不会进行任何拓展。
cat > /etc/rsync.pas <<`EOF`
rsync:$passwd
EOF

shell 字符串相关

shell 只有字符串这一种类型,所有的变量或变量值都可以看作是字符串。

1、获取字符串长度

如果要获取字符串的长度,可以在变量展开的时候,在变量名前面加上 # 来得到。

  • 格式:${#variable}

说明:这种方式获取字符串长度时,需要是一个变量名,在变量展开时得到,而不是对单纯的字符串操作。

例如

if [ ${#new_mysql_passwd} -lt 8 ] ||  [ ${#new_mysql_passwd_confirm} -lt 8 ]; then
	echo "密码长度至少 8 位,请重新输入。"
FI

2、删除字符串前缀

删除字符串前缀是从字符串的开始部分开始匹配,去掉匹配掉的,剩下的做为一个新字符串。

2.1 最短匹配原则

找到满足匹配条件pattern部分的内容就停止查找,然后截取掉pattern的部分,余下的就做为一个新字符串。

格式var=${variable#pattern}

例如

NAME="lct_net_manager.tb_err_future_loc_20%"

# 截取到的值是tb_err_future_loc_20%
tb_name=${NAME#*.}

说明:单个 # 代表是最短匹配,从前向后找,找到满足条件的就终止。这里的匹配条件是找任意字符开头的,直到遇到字符.的部分截取掉,剩下的做为新字符串。

2.2 最长匹配原则

找到满足匹配条件pattern部分的内容后,还会继续找,知道找到最后一个满足条件的,然后截取掉pattern的部分做为一个新字符串。

格式var=${variable##pattern}

3、删除字符串后缀

从字符串的末尾部分开始匹配,去掉匹配掉的,剩下的做为一个新字符串。

3.1 最短匹配原则

从字符串的末尾向前查找,找到符合匹配条件的内容就停止匹配,然后将该部分内容截掉后,剩下的做为一个新字符串。

格式var=${variable%pattern}

3.2 最长匹配原则

从字符串的末尾向前查找,找到符合匹配条件的内容不会停止,而是一直找到字符串的起始字符,将离末尾最远部分满足匹配条件的内容截取掉,剩下的做为一个新字符串。

格式var=${variable%%pattern}

例如:

NAME="lct_net_manager.tb_err_future_loc_20%"

# 删除从 . 开始的所有字符,截取的字符串是lct_net_manager
db_name=${NAME%%.*}

4、匹配字符串内容

通过 shell 中的 [[ ]] 可以检查字符串中是否包含特定的字符。

4.1 通配符形式匹配特定内容
  • 格式[[ string == *substring* ]]
  • 特点:如果使用 == 时,则等号右边的某些元字符会被解释为shell的通配符。

例如

# 如果字符串中以 .tar.gz 结尾
if  [[ "${DAILY_BACKUP_NAME}" == *.tar.gz  ]];then   
4.2 正则形式匹配特定内容
  • 格式:[[ string =~ *substring* ]]
  • 特点:如果使用 =~ 时, 等号右边的某些元字符会被解释为正则表达式元字符,并且默认支持的是扩展正则表达式

例如

 [[ "$PORT" =~ ^[0-9]+$ ]] 

shell 算数运算

shell中虽然只有字符串一种数据类型,但是可以使用数字型字符串(仅包含数字字符0-9)来表示和操作数字,然后通过相应的工具和命令来实现进行算数运算;

  • 表达式:编程中的表达式是由常量、变量、操作符和函数等组成的代码片段,可以通过计算产生一个值;
  • 运算表达式:不包含赋值符号 =的表达式
  • 赋值表达式:包含赋值操作符 =的表达式

1、使用 let 关键字

let 是bash中的一个关键字,利用let 可以实现赋值表达式的算数计算。

格式let "赋值表达式"

特点

  1. 自动解析变量:变量前可以不加$符号对变量进行引用,只能计算整数。
  2. 空格不严格:算数表达式需要用引号保护,参数和操作符之间可以存在空格,也可以没有
  3. 无返回值:直接进行算数计算,将结果赋值给对应的变量。
  4. 只能计算整数,不能计算小数。

例如

a=1
b=2
let "c=a+b"  输出3

2、expr 命令使用

expr 是Linux中的一个命令,可以实现对运算表达式的算数计算。

格式expr "运算表达式"

特点

  1. 有返回值:返回值是计算结果
  2. 空格严格:参数和操作符之间必须要有空格分隔
  3. 不能解析变量:变量需要使用$符引用
  4. 只能计算整数,不能计算小数。

例如

a=1
b=2
echo "$(expr $a + $b)"

3、$(()) 使用说明

$(()) 是shell的一种语法格式,可以实现对运算表达式的算数计算。

格式$(( 运算表达式 ))

特点

  1. 有返回值:返回值是计算结果的值
  2. 自动解析变量:变量前可以不加$符号对变量进行引用,加了也没错
  3. 空格不严格:运算符和参数之间可以有空格,也可以没有

例如

echo $((3 + 2))  # 5

4、bc 命令使用

bc 命令可以实现交互式,也可以实现非交互式操作。shell脚本中一般使用非交互式模式,通过构建一个运算表达式,然后做为bc的输入,bc会输出计算结果。

bc 除了可以进项算出运算外,还可以对数字进行逻辑判断。

非交互式格式

  • 整数运算:echo "运算表达式" | bc
  • 小数运算:echo "scale=num; 运算表达式" | bc -l (通过salce来指定要保留的小数位数)
  • 逻辑运算:echo "逻辑表达式" | bc

特点

1、加上-l选项后可以计算小数,通过scale可以指定保留的小数位置。

2、具有返回值,如果是算数运算,则返回值就是计算结果,如果是逻辑运算,则返回0或1

3、空格不严格:构建的表达式中,参数和运算符号之间可以存在空格,也可以没有。

例如

# 整数运算
echo "3 + 3"|bc

# 进行逻辑运算
$(echo "$util_avg > 90" | bc -l) ))

# 小数计算
$(echo "scale=2; ${iowait_sum} + ${iowait}" | bc -l)

shell 逻辑运算

shell 中没有直接的布尔值类型,但是可以根据命令的退出状态码,即0 表示真,非 0 表示假来实现逻辑判断。

1、短路与(&&)

shell 中的 & 是后台符, 用于将命令放入后台执行,使脚本或终端可以继续执行其他任务,而不会等待该命令完成。实现与运算的是 && 符。

短路与(&&)特点:

  • 当两个条件都为真时,整个表达式为真。只要有一个条件为假,结果就是假。
  • 如果第一个条件为,则不会再评估第二个条件,因为整个表达式已经确定为假了。
  • 如果第一个条件为,才会继续评估第二个条件。

例如

# 前面的执行成功,才会执行后面的
[ $(id -u) -ne 0 ] && echo "need root run this script" && exit 1

2、短路或(||)

shell中的 | 是管道符,用于将前一个命令的输出作为输入传递给下一个命令,实现数据的逐步处理。shell中实现逻辑运算的是 || 符。

短路或(||)特点:

  • 当至少一个条件为真时,整个表达式为真。只有当所有条件都为假时,结果才是假。
  • 如果第一个条件为,则不会再评估第二个条件,因为整个表达式已经确定为真了。
  • 如果第一个条件为,才会继续评估第二个条件。

例如:端口判断

[ "$https_port" -eq 443 ] || ( [ "$https_port" -gt 1024 ] && [ "$https_port" -lt 65536 ] );

3、取反运算(!)

取反运算就是将条件的真假值取反。如果条件为真,使用 ! 后结果为假;如果条件为假,使用 ! 后结果为真。

例如:判断目录是否存在

if [  -d /path/dir ];then
    echo "exsit"
fi

shell 条件判断

1、test 和 [

test 是早期shell中的一个内置命令,通过指定的判断条件来返回一个退出状态码(返回 0 表示判断为真,返回非 0 表示判断为假)。现在的shell 一般使用的是 [ 命令,其更加简洁和容易阅读。在功能上几乎完全和test相同,可以理解为 [ 就是test命令的别名。

命令格式

  • test option expression
  • [ option expression ]

命令特点

  • [ 命令使用时,还需要加一个 ], ] 仅作为语法符号来表示条件表达式的结束位置。
  • 左右方括号 [] 必须用空格与表达式隔开,否则会报错。
1.1 文件判断
  • -e filename:检查文件是否存在
  • -f filename:检查是否为普通文件
  • -d filename:检查是否为目录
  • -s filename:检查文件是否非空

例如

if [ ! -d "${BACKUP_PATH}" ];then
    mkdir -p "${BACKUP_PATH}" && echo "$(date +'%F %T')  ${BACKUP_PATH} 创建成功" | tee -a ${LogFile};
fi
1.2 整数判断
  • -eq:检查两个数值是否相等
  • -ne:检查两个数值是否不等
  • -gt:检查第一个数值是否大于第二个数值
  • -lt:检查第一个数值是否小于第二个数值
  • -ge:检查第一个数值是否大于等于第二个数值
  • -le:检查第一个数值是否小于等于第二个数值

例如

if [[ $(date +%u) -eq 7 ]]; then
    # 今天是周日
    START_OF_WEEK=$(date +'%Y%m%d')
fi
1.3 字符串判断
  • =:检查字符串是否相等,shell中,= 和 == 都是等价。
  • !=:检查字符串是否不等。
  • -z string:检查字符串是否为空,为空则为真。
  • -n string:检查字符串是否非空,非空则为真。

例如:字符串非空则为真

if [ -n "${LATEST_FULL_BACKUP}" ]; then
    LATEST_FULL_BAK_NAME=$(basename "${LATEST_FULL_BACKUP}")
fi
1.4 浮点数判断

浮点数判断不能使用以上两个命令,需要使用bc命令才行,构造表达式时,通过scale来指定小数的位数。

例如

a=1.1
if echo "$a > 2" | bc -l > /dev/null; then
    echo "a >2"
fi

2、(( )) 命令

(( )) 命令是shell中的一个内置命令,可以对整数进行逻辑判断,该命令会返回一个退出状态码(返回 0 表示判断为真,返回非 0 表示判断为假)。通过退出状态码就可以实现对整数的判断。

常用操作符

  • 相等 (==) 、不等 (!=)
  • 大于 (>) 、小于 (<)
  • 大于等于 (>=) 、小于等于 (<=)

例如

a=1
b=2
if ((a >b)) ;then
	echo "a > b"
else
	echo "a < b"
fi

shell 数组相关

shell 支持普通数组和关联数组这两种类似的数组。

  • 普通数组:通过数字索引来管理元素,索引是从0开始的连续整数。

  • 关联数组:允许使用字符串作为索引,可以自定义数组的索引。

1、申明数组规则

数组名和shell普通变量名一样,也是由数字、字母和下划线组成,且不能以数字开头,否则会报错。

申明普通数组

  • 标准格式:declare -a 数组名
  • 简化格式:数组名=()

申明关联数组

  • 标准格式:declare -A 数组名(申明关联数组不能简化,只能用标准格式)

2、数组元素赋值

2.1 普通数组赋值

定义时赋值:定义数组的同时直接赋值时,会自动根据IFS指定的分隔符(空格、制表符和换行符)作为分隔符来划分每个元素

例如:

# IFS 会按照空格来进行分词,此时该数组有三个元素
PATTERNS=(
    "lct_net_manager.tb_err_future_loc_20%"
    "position_ehcommon.tb_blood_pressure_history_%"
    "only_door.tb_person_location_history"
    )

逐个元素赋值:通过指定数组的索引,然后进行赋值。

例如

arry=()
arry[5]=18
echo ${arry[5]}

命令替换赋值:通过将命令输出作为数组元素,命令输出后也会受IFS的影响,shell会根据IFS定义的分隔符来进行分词处理,每个单词就是数组的一个元素。

例如

arry=()
arry=($(ls -d /root/*))
for name in ${arry[@]} ;do
   echo $name
done
2.2 关联数组数组

因为关联数组的索引是自定义的,并非普通数组那样是自增的整数,所以给关联数组赋值时,需要指定对应的索引,索引一般用引号保护。避免包含特殊字符或空格导致的错误。

格式为:["key"]="value"

例如

# 必须显式申明
declare  -A arry
arry=(["name"]=tom [age]=18 [sex]=man [number]=112)
echo ${arry[name]}

例如

key_size=$(redis-cli  -a redhat -n ${db_num} MEMORY USAGE "$key_name")

# redis的键名做为key 键值作为value
key_arry["$key_name"]=$key_size

3、操作数组元素

数组的常见操作

  • 获取指定元素的值:${数组名[索引]}
  • 获取数组所有元素:${arry_name[@]}
  • 获取数组所有索引:${!arry_name[@]}
  • 获取数组的元素个数:${#arry_name[@]}
  • 设置数组的默认值:${arry[@]:-defaults}
  • 编译数组所有元素:通过for循环实现

例如:便利某个数组的所有元素

for i in "${!fruits[@]}"; do
    echo "索引 $i 对应的元素是:${fruits[i]}"
done

例如:如果数组为空或未定义,就返回对应值

${arry[@]:-22 80 8000 8001 3306 9100 9001 1883 1884 9802}

shell 语句结构

1、分支结构

1.1 if 结构

说明:if 语句中的条件必须是一个能返回退出状态的命令

格式

# **单分支**
if 条件; then
    Command;
fi

# **双分支**
if 条件; then
    Command;
else
    Command;
fi

# **多分支**
if 条件;  then
    Command;
elif 条件; then
    Command;
else
    Command;
fi

例如

# [等价于命令test,if会根据这个命令的退出状态来进行分支选择

if [ 0 -ne 0 ]; then
    echo "条件为真"
else
    echo "条件为假"
fi

# 例如
if systemctl restart mysql ; then
	echo "mysql 重启成功"
else
	echo  "mysql 重启失败"
fi
1.2 case 结构

在 shell 的 case 结构中,不需要像其他编程语言(如 C 或 Java)那样使用 break。原因是 shell 的 case 语句在匹配到一个模式后,会自动退出整个 case 块,不会继续执行后面的分支。

格式

case 变量 in  
    模式1)
        # 当变量匹配模式1时执行的代码
        break
        ;;
    模式2)
        # 当变量匹配模式2时执行的代码
        break
        ;;
    *)
        # 以上模式都不匹配时执行的代码
        break
        ;;
esac

例如:实现一个选择菜单

#!/bin/bash

read -p "请输入操作(start、stop 或 restart): " action

case "$action" in
    start)
        echo "系统正在启动..."
        ;;
    stop)
        echo "系统正在关闭..."
        ;;
    restart|reload)
        echo "系统正在重启..."
        ;;
    *)
        echo "无效的操作,请输入 start、stop 或 restart。"
        ;;
esac

2、循环结构

2.1 for 循环
2.1.1 普通结构

格式

for 变量  in 一个列表(使用IFS定义的分隔符分隔) ;do
    command;
done

例如

#!/bin/bash

for name in tom bob alice ;do
	echo $name
done
2.1.2 for 结合 (( ))

这种方式一般用于处理算术表达式和整数运算

格式

for ((表达式)) ;do
    command;
done

例如:实现乘法表

#!/bin/bash
for (( i=1; i<=9; i++ )) ;do
    for (( j=1; j<=i; j++ ));do
        echo -ne "$i * $j = $(expr $i \* $j) \t"
        if [ $i -eq $j ];then
            echo
        fi
    done
done
  • 初始化表达式:在循环开始前执行,通常用于初始化循环变量。
  • 终止条件表达式:在每次循环迭代之前检查。如果条件为真(即非零值),则继续循环;
  • 步进表达式:用于更新循环变量的值。
2.2 while 循环

while循环的条件必须是一个能返回退出状态的命令

2.2.1 while 普通结构

格式

while 条件 ;do
	# command
done

例如

  # true是shell中的一个特殊命令,主要作用是返回一个真值,即无论何时运行true命令,它都会返回一个成功的退出状态
 while true ; do
        read -r -p "请输入mosquito_1884新密码: " mqtt1884_passwd
        read -rp "再次输入mosquito_1884新密码: " mqtt1884_passwd_confirm
        if [ -z "${mqtt1884_passwd}" ] || [ -z "${mqtt1884_passwd_confirm}" ];then
            echo "输入无效,密码不能为空。"
        elif [ ${#mqtt1884_passwd} -lt 8 ] ||  [ ${#mqtt1884_passwd_confirm} -lt 8 ]; then
            echo "密码长度至少 8 位,请重新输入。"
        elif [ "${mqtt1884_passwd}" != "${mqtt1884_passwd_confirm}" ]; then
            echo "两次输入的密码不一致,请重新输入"
        else
            break
        fi
    done
2.2.2 while 结合 read

while 循环结合 read 命令一般用于逐行读取文件或命令的输出,并对每一行进行处理。

格式

while read [option] Var  ;do
    # commmand
done

例如

#!/bin/bash
while read -rp "请输入数字: " number ;do
	echo $number
done

# 
请输入数字: 1
1
请输入数字: 2
2
请输入数字: 3
3
请输入数字: ^C
2.2.3 while 结合 read 和 |

当某个命令有多行输出时,通过管道符将输出做为read的输入,从而实现逐行处理,然后通过while来定义处理的指令。

但是通过这种方式,管道符后面的while语句块会启动也给子shell来执行,所以如果涉及到对父shell中变量的处理,则子shell不能改变父shell的值。

格式

command | while read [option] vAR ;do
	# command
done

例如

#!/bin/bash
sum=0
ls -d /home/* | while read dir ;do
    sum=$((sum+1))
    echo $dir
    echo $sum
done
echo $sum  # 输出0,因为子shell的修改不会影响父shell
2.2.4 while 结合 read 和 <()

"<()"叫作进程替换,用于将括号中命令的输出存储到一个文件描述符(通常是一个命名管道或临时文件)中,然后其他命令可以像读取普通文件一样读取命令的输出。常用于那些需要文件名参数的命令

通过 进程替换结合while 使用,就不会像使用管道符一样开启子进程的问题。再通过 read 来逐行读取内容给while中的指令处理。

格式

while read 变量名 ;do
	# command
done < <(command)

例如

while read -r device _ _ _ _ r_await _ _ _ _ _ w_await _ _ _ _ _ _ _ aqusz util ;do
    echo "Device: $device, r_await: $r_await, w_await: $w_await"
    # 将不同设备的信息分别存入数据
done < <(iostat -dx 1 1 | grep -Ev '^Linux|^Device|^$')
2.3 select 循环

通过 select 可以在shell中用于生成简单交互式菜单的命令,让用户通过输入选项编号来选择其中之一。select 一般结合 PS3变量使用,通过PS3变量可以指定 select 命令的提示符,让用户知道该输入选项编号。

格式

select 变量 in 一个列表 ;do
	# command
done

例如:实现一个选择菜单

PS3="请选择指定序号:"
select  opt in "MySQL" "Redis" "Mqtt1884" "Mqtt1883" ;do
    case $opt in
        "MySQL")
            set_mysql
            break  # 这里的break用于退出select循环
            ;;
        "Redis")
            set_redis
            break
            ;;
        "Mqtt1884")
            set_mqtt1884
            break
            ;;
        "Mqtt1883")
            set_mqtt1883
            break
            ;;
        *)
            echo "无效的选择,请重试"
            ;;
    esac
done
2.3 break 和 continule
  • break 命令:终止整个循环。当执行到 break 命令时,循环立即结束,程序控制流跳出循环体,继续执行循环后面的代码。
  • continue 命令:跳过当前循环中余下的命令,立即进入下一次循环迭代。当执行到 continue 命令时,当前迭代剩下的命令不会执行,循环直接进入下一次迭代的判断。

shell 函数

shell中的函数本质上就是一个语句块,它将一系列命令封装起来,使得可以通过函数名来重复调用这些命令。

1、定义函数

完整格式

function fun_name()
{
    command;
}

简写格式

# 省略小括号:
function fun_name
{
	command;
	command;
}

# 省略function 关键字:
fun_name() 
{
	command;
	command;
}

2、调用函数

说明:shell 脚本是从上到下依次执行的,如果在调用函数之前没有定义,shell 会提示“未找到命令”或类似的错误。所以需要先定义函数才能调用函数。

  • 无参调用:直接通过函数名即可调用函数
  • 传参调用:函数名后面跟参数,参数一般使用空格分隔。函数内通过 $1$2 等来引用这些参数

3、函数返回值

调用函数后,命令状态退出码($?) 就是函数的返回值,默认情况下函数的返回值就是最后一条命令执行状态的退出码,也可以退出return 命令来指定函数的退出状态码。

3.1 return 命令

格式:return [0-255]

作用

  • 函数退出状态码:在函数的末尾可以通过return 来定义函数的退出状态码。
  • **退出函数执行:**如果在函数的其他位置使用,则当函数执行到 return 时,会立即退出函数并返回控制权给调用者,函数后续的指令不会被执行。

说明:return 命令一般在函数内部使用。

shell read 命令

通过 read 命令可以从标准输入(通常是键盘)读取一行数据,并将其存储到一个或多个变量中。

说明:当read 指定多个变量的时候,shell默认会根据IFS定义的分隔符来对用户的标准输入字符串进行分词处理,然后赋值给对应位置的变量。

格式read [option] var1 .. varn

常用选项:

  • -p:在读取输入前输出提示信息,例如: -p “请输入密码”
  • -a:将用户的多个输入,以使用 IFS(内部字段分隔符)中的字符来拆分输入存入数组。例如:read -a words
  • -n:只读取指定的字符数就结束,不必等到回车键。例如: -n 1 就只会读取用户输入的第一个字符,然后自动结束。
  • -r:关闭反斜杠转义,使用户输入的反斜杠保持原样

例如

read -rp "请输入当前 redis 的密码(default: eHIGH2014): "  current_redis_passwd
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只小爪子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值