简介
GNU Bash,又名 Bourne Again Shell。它最初发布于 1989 年,并且轻松成长为 Linux 世界中使用最广泛的 shell,甚至常见于其他一些类 Unix 系统当中。
变量
shell中的变量都是全局变量,函数中的变量需要使用 local
将其变成局部变量,防止污染函数外的变量。
不过从严格意义上,Bash没有变量类型。Bash中的变量,在运行的时候会被展开成其对应的值(字符串)。
静态变量
在执行过程中不能改变的变量
readonly passwd_file=”/etc/passwd” readonly group_file=”/etc/group”
变量操作
- 大小写切换
^大写,,小写, ~大小写切换
重复一次只匹配一个字母,重复两次则应用于所有字母。
HI=HellO echo "$HI" # HellO echo ${HI^} # HellO echo ${HI^^} # HELLO echo ${HI,} # hellO echo ${HI,,} # hello echo ${HI~} # hellO echo ${HI~~} #hELLo
- 替换运算符
${var:-word} # 如果var存在且非null,返回它的值;否则返回word ${var:=word} # 如果var存在且非null,返回它的值;否则将word赋值给var,并返回var的值 ${var:?word} # 如果var存在且非null,返回它的值;否则显示var:word ${var:+word} # 如果var存在且非null,返回word;否则返回null
冒号(:)可省略
- 模式匹配运算符
${var#pattern} 匹配前缀(最小匹配),并返回余下内容 ${var##pattern} 匹配前缀(最大匹配),并返回余下内容 ${var%pattern} 匹配结尾(最小匹配),并返回余下内容 ${var%%pattern} 匹配结尾(最大匹配),并返回余下内容
pattern为正则表达式匹配
数组
Bash 提供了一维数组变量。任何变量都可以作为一个数组;内建命令 declare 可以显式地定义数组。数组的大小没有上限,也没有限制在连续对成员引用和赋值时有什么要求。数组以整数为下标,从 0 开始。
- 定义和初始化数组
declare -a array # 显示声明了数组array array[key]=value # array[0]=one array=(value1 value2...) # value的形式都是[subscript]=string,下标和等号可以省略,示例如下。 array=([0]=value1 [2]=value3 [3]=value[4]) # 关联数组的另一种定义方式 mydict=(["name"]=guess ["old"]=18 ["favourite"]=coconut ["my description"]="I am a student")
从上面来看数组的定义也是非常灵活多变的,能够满足我们大部分的需求,跟其其它语言最大的区别就是shell中的数组大小没有上限,也可以理解为数组是动态的。
- 数组的访问
数组的任何元素都可以用${array[subscript]}
来引用,花括号是必须的,以避免和路径扩展冲突。
如果 subscript 是@
或是*
,它扩展为array的所有成员。
这两种下标只有在双引号中才不同。在双引号中,"${name[*]}"
扩展为一个词,由所有数组成员的值组成,用特殊变量IFS的第一个字符分隔数组成员;"${array[@]}"
将array的每个成员扩展为一个词。 如果数组没有成员,${name[@]}
扩展为空串。
示例--"${name[*]}"
和"${array[@]}"
的不同
#!/bin/bash arr=("one" "two") for i in "${arr[*]}" do echo ${i} done for i in "${arr[@]}" do echo ${i} done
输出如下
one two one two
- 数组的删除
用unset来进行数组的删除
unset array[2] # 删除第三个成员 unset array # 删除整个数组
- 数组的长度
${#arr[@]} ${#arr[*]} ${#arr} #错误的。这个获取的是数组第一个成员的长度。
- 数组的”切片”操作
获取数组的“子串“用${arr[@]:n:m}
来表示,如果没有:m那么就获取从下标n开始到最后一个元素的“字串“,示例如下:
#!/bin/bash arr=(one two three four) echo ${arr[@]:2} echo ${arr[@]:1:3} echo ${arr[@]:0}
输出如下
three four two three four one two three four
- 关联数组
shell中还可以声明一个关联数组,普通数组只能使用整数作为数组的索引,而关联数组则使用字符串作为数组的索引。这个关联数组有点像其它语言中的字典。在mac下的bash 中的declare不含这个-A
这个参数。
#!/bin/bash declare -A array array["age"]=29 array["name"]=Yang # 输出数组的value echo "${array[@]}" # 遍历数组的值 for a in "${array[@]}"; do echo "${a}" done # 通过下标获取元素值 echo "${array[age]}" # 输出数组的key echo "${!array[@]}" # 遍历数组的key for a in "${!array[@]}"; do echo "${a}" done
输出
Yang 29 Yang 29 29 name age name
应用
-
判断一个指定的字符串是否在该数组中
if echo "${ARR[@]}" | grep -w "item_1" &>/dev/null; then echo "Found" fi
四则运算
- bash支持的算数运算
+ - * /
有些场景中,乘法符号需要转义
- 算数运算实现方式
let a=a+b 计算结果无法直接获取,需要赋值后才能使用 var=$[算数表达式] 计算结果可以直接使用,建议使用 var=$((算数表达式)) 同上 var=$(expr arg1 arg2 arg3)
- 浮点数计算
使用shell
内置命令bc
# 进制转换 echo "obase=2; ibase=2; 1+1" | bc # 保留精度 echo "scale=2;1/2" | bc # 多行计算 # v1=$(bc << EOF > v2=1 > v3=2 > v2+v3 > EOF > ) # echo $v1 3
- 增强型赋值
+= -= *= /= %=
let var=var1+=1 let var++ # 自增 let var-- # 自减
条件测试
分类
- 整数测试
- 文件测试
- 字符测试
真返回值为true或者false
条件比较测试表达式有以下三种
[ expression ] [[ expression ]] test expression
整数测试
-eq:= -ne:!= -gt:> -lt:< -ge:>= -le:<=
文件测试
- 存在性测试
-e file 是否存在 -f file 是否为普通文件 -d file 是否为目录
- 权限测试
-r file 指定文件对当前用户是否可读 -w file 指定文件对当前用户是否可写 -x file 指定文件对当前用户是否可执行 -u file 当前用户是否是文件的属主 -g file 当前用户是否是文件的属组
- 文件大小测试
-s file 文件存在且非空
字符测试
[[ ]]
或者[ ]
都可以
- 等值比较
=
或者==
,注意:等号两端要有空格
- 不等比较
!=
,注意:等号两端要有空格
- 是否为空测试
-z string:测试字符串是否为空,空为真; -n string:测试字符串是否不为空,不空为真; ~= 左侧的字符串能否被右侧的PATTERN所匹配
组合条件测试
主要分两类
COMMAND1 && COMMAND2 COMMAND1 || COMMAND2 ! COMMAND1 [ EXPRESSION1 -a EXPRESSION2 ] [ EXPRESSION1 -o EXPRESSION2 ] [ ! EXPRESSION1 ]
示例,传递一个用户名参数给脚本,判断此用户的用户名跟其基本组的组名是否一致,并将结果显示出来。
#!/bin/bash if [ $# -ne 1 ]; then echo "Please enter a argument." exit 1 elif ! id $1 &> /dev/null; then echo "No such user." exit 2 elif [ $1 == $(id $1 -g -n) ]; then echo "Same." else echo "Diffrent." fi
选择语句
case SWITCH in value1) statement1 ... ;; value2) statement2 ... ;; *) statement3 ... ;; esac
循环语句
循环需要有进入条件和退出条件
for--有限循环
# 形式1 for 变量 in 列表;do 循环体 done # 形式2 for (( expr1 ; expr2 ; expr3 )); do 循环体 done
生成整数列表
{1..100} `seq [起始数] [步进长度] 结束数`
示例
for i in `seq 1 $a`; do echo $i; done
while--无线循环
条件满足则执行循环
while CONDITION; do 循环体 done
示例
while的特殊用法一,死循环
while :; do 循环体 done
while的特殊用法二,按行读取文件
while read LINE; do 循环体 done < /PATH/TO/SOMEFILE
until
满足条件则结束循环
until CONDITION; do 循环体 done
continue
提前结束本轮循环,进入下一轮循环
函数
- 通过位置传递参数
- 通过 echo 返回值
- 通过return 返回状态码
-
定义
function func_name(){ ...函数体... }
-
直接通过函数名调用,函数名后不用加括号。
格式化输出 echo printf
echo
-n 不换行输出 -e 支持扩展
printf
使用printf可以输出更规则更格式化的结果。它引用于C语言的printf命令,但是有些许区别。
printf可以指定字符串的宽度、实现左对齐(使用减符号-)、右对齐(默认的)、格式化小数输出等。
使用printf最需要注意的两点是:
- printf默认不在结尾加换行符,它不像echo一样,所以要手动加“\n”换号;
- printf只是格式化输出,不会改变任何结果,所以在格式化浮点数的输出时,浮点数结果是不变的,仅仅只是改变了显示的结果。
> printf "%-5s %-10s %-4s\n" No Name Mark # 三个%分别对应后面的三个参数 > printf "%-5s %-10s %-4.2f\n" 1 Sarath 80.34 # 减号“-”表示左对齐 > printf "%-5s %-10s %-4.2f\n" 2 James 90.998 # 5s表示第一个参数占用5个字符 > printf "%-5s %-10s %-4.2f\n" 3 Jeff 77.564
其他
位置参数 $@ $* $#
$*
表示从1开始所有位置的参数,如果扩展发生在双引号内,即"$*"
,则扩展包含每个参数值的单词,每个参数值用特殊表量IFS的第一个字符分割;也就是说,"$*"
等价于"$1c$2c..."
,其中,c时特殊变量IFS的第一个字符。如果变量IFS没有定义,则参数之间默认用空格分割。
$@
也扩展为从1开始的所有位置参数。但当它的扩展发生在双引号内时,每个参数都扩展为分割的单词。即:"$@"
等价于"$1"、"$2" ...
。参数@与*之间的区别会在for循环中体现出来。循环时用 $@
。
如果命令运行失败让脚本退出执行
set -o errexit set -e
若有用未设置的变量即让脚本退出执行
set -o nounset set -u
来源:https://www.cnblogs.com/hiyang/p/12611836.html