0. shell脚本执行

可使用自动显示git 分支的脚本来验证

1. source

通过source命令执行脚本,脚本可无执行权限。
表示在当前bash环境下执行脚本

  1. source xxx.sh

2. .

. 与source等效

  1. . xxx.sh

3. ./

打开一个子shell来读取并执行脚本中的命令

  1. ./xxx.sh
  • 每个shell脚本有效地运行在父shell(parent shell)的一个子进程里. 这个父shell是指在一个控制终端或在一个xterm窗口中给你命令指示符的进程.shell脚本也可以启动他自已的子进程. 这些子shell(即子进程)使脚本并行地,有效率地地同时运行脚本内的多个子任务.
  • export 导出环境变量是单向传递,可从父进程传递到子进程,不可从子进程传递到父进程。

    1. 特殊字符

    1. 替换符

    1. 变量替换

    - # 注释
  • —— 注释

    1. # This line is a comment.
    2. echo "hello" #test
  • $ —— 取变量值

  1. str="hello"
  2. echo $str
  3. //结果输出:hello
  • ${} —— 取变量值

    1. name="IRON"
    2. echo I am $nameMAN
    3. //结果输出:I am IRONMAN
  • ${v1:-v1} 、${v1:+v2}和$(v1:=v2) —— 三目运算

表示如果变量 var1 为空或已被删除(unset),返回$var2,否则返回var1的值,var1值不变
var1 与var2互斥。

  1. var1=1
  2. echo ${var1:-$var2}
  3. //结果输出:1
  1. var2=2
  2. echo ${var1:-$var2}
  3. //结果输出:2

表示如果变量 var1 被定义且不为空,那么返回$var2,否则返回空,var1值不变
var1 与var2共存

  1. var2=2
  2. var1=1
  3. echo ${var1:-$var2}
  4. //结果输出:2

如果变量 var1 为空或已被删除(unset),那么返回 word,并将 var1 的值设置为var2,否则返回var1

  1. var2=2
  2. var1=
  3. #unset var1
  4. echo ${var1:=$var2}
  5. echo $var1
  6. //结果输出:2 2
  • ${#} —— 获取变量长度
  1. str="abc"
  2. echo ${#str}
  3. //结果输出:3
  • ${:}、${::} —— 字符串提取

字符串提取,用法${var:n}。若n为正数,n从0开始,表示在变量var中提取第n个字符到末尾的所有字符。若n为负数,提取字符串最后面n的绝对值个字符,使用时在冒号后面加空格或一个算术表达式或整个num加上括号,如${var: -2}、${var:1−3}或 ${var:(-2)}均表示提取字符串最后两个字符

  1. str="abcdef"
  2. echo ${str:0}
  3. echo ${str:1}
  4. echo ${str:1-3}
  5. //结果输出: abcdef bcdef ef

${var:n:m},n是字符串索引起始,m为截取长度

  1. str="abcdef"
  2. echo ${str:0:3}
  3. //结果输出:abc
  • {,}、{..}—— 扩展
  1. echo {1,2,3}.txt
  2. echo {1..3}.txt
  3. //结果输出:1.txt 2.txt 3.txt
  4. 1.txt 2.txt 3.txt
  • ${//}、${///} —— 模式匹配替换

模式匹配替换。${var/pattern/pattern}表示将var字符串的第一个匹配的pattern替换为另一个pattern。不改变原变量。

  1. str="123abc123abc"
  2. echo ${str/123/456}
  3. echo ${str//123/456}
  4. echo $str
  5. //结果输出:456abc123abc
  6. 456abc456abc
  7. 123abc123abc

2. 命令替换符

  • ``和$()

在反引号 中的内容通常是命令行,程序会优先执行反引号中的内容,并使用运行结果替换掉反引号处的内容(替换命令的输出结果)
$()与``等效

  1. name=`uname -s`#等效name=$(unmae -s)
  2. echo $name
  3. //结果输出:Linux

2. 字符串符

‘’单引号

单引号括住的内容,被视为常量字符串,引号内的禁止变量扩展,并且单引号字符串中不能出现单引号(对单引号使用转义符后也不行)

“”双引号

两个双引号。双引号包围的内容可以允许变量扩展,可以包含双引号,但需要转义

  1. str="IRON"
  2. echo '$str MON'#不允许扩展
  3. echo "$str MON"#允许扩展
  4. //结果输出:$str MON
  5. IRON MON

3. 功能符

1. 语法功能符

  • —— 注释(见替换符)

    1. # This line is a comment.
    2. echo "hello" #test
  • ;—— 语句分隔符

分号,语句的分隔符。在shell文件一行写多条语句时,使用分号分割

  • ;; case —— 分隔

    1. name="Mike"
    2. case $name in #case后为取值,值后为关键字 in
    3. "Jim")
    4. echo "case 1:my name is $name"
    5. ;;
    6. "Mike")
    7. echo "case 1:my name is $name"
    8. ;;
    9. *)# *会捕获所有与已知模式不匹配的值
    10. echo "case *:unknown name $name"
    11. ;;
    12. esac # case与esac配对使用,esac时case反写值,类型if-fi
  • 、>> —— 文件重定向

操作对象是文件

  1. ls >1.txt
  2. //等效,标准输出重定向到文件中
  3. ls 1>1.txt
  • & ——描述符重定向

  1. 2>&1
  2. //表示把 标准错误输出 重定向到 标准输出, 这在控制台下看到的效果 2>&1 和 1>&2 可能是一样的,
  3. 因为标准输出或标准错误输出的目的地默认都为屏幕。
  • <<<

三个小于号,作用就是将后面的内容作为前面命令的标准输入

  • ()
      1. 命令替换时使用
    • 用与数组
  • {} —— 语句块
    • 函数中使用
  1. fun1()
  2. {
  3. echo "hello world"
  4. }
  5. fun1
  6. //输出结果:hello world

4. 运算符

1. 数据运算符

  • +、-、*、/
  • % —— 取余
  • = —— 赋值
  • (()) —— 是shell中算数及赋值运算的扩展

另一个对任何编程语言都很重要的特性是操作数字的能力。遗憾的是,对shell脚本来说,这个处理过程会比较麻 烦。在shell脚本中有两种途径来进行数学运算。
特点:

  • 在双括号结构中,所有表达式可以像c语言一样,如:a++,b—等。
  • 在双括号结构中,所有变量可以不加入:“$”符号前缀。
  • 双括号可以进行逻辑运算,四则运算
  • 双括号结构 扩展了for,while,if条件测试运算
  • 支持多个表达式运算,各个表达式之间可以使用”,”分开 ```c

    for 循环

    for((i=0;i<10;i++ )) do echo $i done

条件判断

a=3 b=3
if (( $a == $b));then echo equal fi

  1. <a name="Xcixz"></a>
  2. #### 2. 关系运算符(用于数值比较)
  3. - [] —— 用于判断条件是否成立,如[ $a == $b ],注意保留空格
  4. - [[]]—— 是对[]的扩展,允许使用<、>、&&、|| 等运算符
  5. - ==/-eq —— 判断是否相等
  6. - -=/-ne —— 判断是否不相等
  7. - -gt —— 判断是否大于
  8. - -ge —— 判断是否大于等于
  9. - -lt —— 判断是否小于
  10. - -le —— 判断是否小于等于
  11. <a name="Y28at"></a>
  12. #### 3. 逻辑运算符
  13. - -a —— 逻辑与
  14. - -o —— 逻辑或
  15. - -! —— 逻辑非
  16. - || —— 1. 用于条件判断时,与[[]]配合使用; 2. 用与命令连接
  17. - && —— 同 ||
  18. <a name="zzxSK"></a>
  19. #### 4. 字符串运算符(字符串比较)
  20. - = ,==应该也可以
  21. - !=
  22. - -z —— 字符串长度是否为0
  23. - -n —— 字符串长度是否为非0
  24. -n 需注意双引号参考:<br />[https://blog.51cto.com/kunge/1585898](https://blog.51cto.com/kunge/1585898)
  25. <a name="s7ysS"></a>
  26. #### 5. 文件运算符
  27. - -d —— 检查file是否存在并是一个目录
  28. - -e —— 检查file是否存在
  29. - -f —— 检查file是否存在并是一个文件
  30. - -r ——检查file是否存在并可读
  31. - -s —— 检查file是否存在并非空
  32. - -w —— 检查file是否存在并可写
  33. - -x —— 检查file是否存在并可执行
  34. - -O —— 检查file是否存在并属当前用户所有
  35. - -G —— 检查file是否存在并且默认组与当前用户相同
  36. - -nt —— file1 -nt file2 检查file1是否比file2新(修改时间)
  37. - -ot —— file1 -ot file2 检查file1是否比file2旧 (修改时间)
  38. - -ef —— 两个文件是否为同一个文件,主要看文件设备号与 inode 是否一致
  39. <a name="WvYZ8"></a>
  40. ### 5. 数组
  41. <a name="vSCX8"></a>
  42. #### 1. 索引数组
  43. bash shell中只支持一维数组,Shell 数组用括号来表示,元素用"空格"符号分割开
  44. ```shell
  45. array=(a b c)
  46. echo ${array[0]}
  47. echo ${array[1]}
  48. echo ${array[2]}
  49. //输出结果:a b c
  1. array[0]=a
  2. array[1]=b
  3. array[2]=c
  4. echo ${array[0]}
  5. echo ${array[1]}
  6. echo ${array[2]}
  7. //输出结果:a b c

获取所有元素

  1. echo ${array[*]}
  2. echo ${array[@]}

获取数组元素个数

  1. echo ${#array[*]}
  2. echo ${#array[@]}

2. 关联数组

关联数组又称为字典。在使用关联数组之前,需要使用命令 declare -A xxx 对其进行显示声明。
关联数组和索引数组除了索引方式不同,其它完全一致。

  1. declare -A array
  2. array["Jack"]=22
  3. array["Mike"]=23
  4. array["Dog"]=26
  5. echo ${array["Jack"]}
  6. echo ${array["Mike"]}
  7. echo ${array["Dog"]}

6. [] 条件判断

第一个方括号后和第二个方括号前都要加一个空格,否则会报错
判断数值或字符串释放相等应该都可以使用==或=和!=

1. 数值比较
  1. n1 -eq n2 检查n1是否与n2相等
  2. n1 -ge n2 检查n1是否大于或等于n2
  3. n1 -gt n2 检查n1是否大于n2
  4. n1 -le n2 检查n1是否小于或等于n2
  5. n1 -lt n2 检查n1是否小于n2
  6. n1 -ne n2 检查n1是否不等于n2

2. 字符串比较
  1. str1 = str2 检查str1是否和str2相同
  2. str1 != str2 检查str1是否和str2不同
  3. str1 < str2 检查str1是否比str2
  4. str1 > str2 检查str1是否比str2
  5. -n str1 检查str1的长度是否非0
  6. -z str1 检查str1的长度是否为0

3. 文件判断
  1. -d file 检查file是否存在并是一个目录
  2. -e file 检查file是否存在
  3. -f file 检查file是否存在并是一个文件
  4. -r file 检查file是否存在并可读
  5. -s file 检查file是否存在并非空
  6. -w file 检查file是否存在并可写
  7. -x file 检查file是否存在并可执行
  8. -O file 检查file是否存在并属当前用户所有
  9. -G file 检查file是否存在并且默认组与当前用户相
  10. file1 -nt file2 检查file1是否比file2
  11. file1 -ot file2 检查file1是否比file2

7. 特殊变量

$n

传递给脚本或函数的参数,n 是一个数字,表示第几个参数,$0 当前脚本名称。
参数从第10个开始$n无法正常获取到参数,需添加括号${n},为保持格式一致,小于10的也可使用${n}

$

传递给脚本或函数的参数个数,不包括$0

$*、$@

传递给脚本或函数的所有参数,不包括$0
$@ 与 $ 的区别,不被双引号””包含时,都以 $1 $2 … $n 的形式输出所有参数
但是当它们被双引号 “” 包含时,`”$
会将所有的参数作为一个整体,以“$1 $2 … $n”的形式输出所有参数。但是“$@”$@一样,还是会将各个参数分开,以$1 $2 … $n`的形式输出所有参数

$?

上个命令的退出状态,或函数的返回值

$$

当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID

8. 数据类型

shell 是一门若类型语言,定义变量时不需要指定数据类型,而是根据上下文自动确定。
shell数据类型属于有字符串和整型
字符串可以使用单引号’ ‘,双引号” “。

9. 注意事项

1. shell中变量赋值=前后不允许后空格

  1. TEST_PATH=libpcap #ok
  2. TEST_PATH= libpcap #ng
  3. TEST_PATH =libpcap #ng

3. 在其它目录执行脚本与在脚本目录中执行脚本存在差异

可在脚本中cd到当前路径中再执行
${BASH_SOURCE[0]}与$BASH_SOURCE与$0可能等效

  1. #!/bin/bash
  2. set -e
  3. #脚本文件名(相对于执行路径(非脚本所在路径))
  4. curfile=$0
  5. #脚本文件路径(相对于执行路径(非脚本所在路径))
  6. curdir=$(dirname $curfile)
  7. #脚本文件绝对路径(pwd获取绝对路径)
  8. path=$(cd $(dirname $0);pwd)
  9. echo curfile:$curfile
  10. echo curdir:$curdir
  11. echo $path
  12. #curfile:./abc/test.sh
  13. #curdir:./abc
  14. #/home/admi/work/slj/abc
  15. filepath=$(cd "$(dirname "$0")"; pwd)
  16. cd $filepaht

3. 遍历参数

  1. for i in $*
  2. do
  3. echo $i
  4. done

5. 为什么要使用 x$1 != x 这种方式来比较呢?想像一下这种方式比较:

if [ -n $1 ] #$1不为空
但如果用户不传参数的时候,$1为空,这时 就会变成 -n ,所以需要加一个辅助字符串来进行比较。
另一种方案时对变量添加双引号 if [ -n “$1” ] 即可
Makefile中条件变量允许为空,shell中不允许

6. 条件/选择语句

  1. if [ condition1 ]
  2. then
  3. command1
  4. elif [ condition2 ]
  5. command2
  6. else
  7. commandN
  8. fi

7. 循环

1. for循环
  1. array=(a b c d e)
  2. # 遍历所有元素
  3. for i in ${array[*]}
  4. do
  5. echo $i
  6. done
  7. //结果输出: a b c d e
  1. # 通过数据元素个数遍历
  2. for(( i = 0; i< ${#TARGETS[*]}; i++))
  3. do
  4. echo "[$i] ${TARGETS[i]}"
  5. done

2. while 循环
  1. array=(a b c d e)
  2. # 通过数据元素个数遍历
  3. while ((i < ${#array[@]}))
  4. do
  5. echo ${array[i]}
  6. ((i++))
  7. done
  1. # 通过数据元素个数遍历
  2. for(( i = 0; i< ${#TARGETS[*]}; i++))
  3. do
  4. echo "[$i] ${TARGETS[i]}"
  5. done

9. 比较关心的功能

basename

为basename指定一个路径,basename命令会删掉所有的前缀包括最后一个slash(‘/’)字符,然后将字符串显示出来。

basename [pathname] [suffix]
basename [string] [suffix]

suffix为后缀,如果suffix被指定了,basename会将pathname或string中的suffix去掉。

  1. file=abc.tar.gz
  2. echo $(basename $file .tar.gz)
  3. #输出abc
  4. path_file=/home/centos/abc.tar.gz
  5. echo $(basename $path_file)
  6. #输出abc.tar.gz

获取文件名

字符串拼接

Shell字符串拼接不需要使用任何运算符,将两个字符串并排放在一起即可,简单粗暴。

文件名和后缀

  1. file=abc.pcap
  2. echo "filename: ${file%.*}"
  3. #输出 abc
  4. echo "suffix : ${file##*.}"
  5. # 输出pcap

行号

  1. echo $LINENO

与其它shell脚本或makefile变量共享

或通过参数定义

参考文献

[1] https://blog.csdn.net/K346K346/article/details/51819236
[2] https://yanbin.blog/linux-input-output-redirection/ 重定向
[3] https://www.jianshu.com/p/3e1eaaa3fee8
[4] https://www.runoob.com/linux/linux-shell-array.html