linux-shellscript相关
章节目录

1. shell 脚本执行的几种方法
2. shell 脚本规范
3. 变量的设置与取消
4. 变量定义
4.1. 普通变量定义
4.2. 变量名定义要求
4.3. 示例
5. 特殊、内置变量
6. 常用操作表达式
7. 变量替换表
8. 常见的运算符
8.1. 变量运算符
8.2. 算术运算符
8.3. 关系运算符
8.4. 条件判断符
8.5. 标准I/O重定向
9. 变量的数值运算
10. 变量读入
10.1. read bash内置变量
11. 条件测试与比较
11.1. 方法
11.2. 说明
11.3. 补充
12. 函数
12.1. 语法
12.2. 调用
13. 判断分支
14. 循环
14.1. while 循环
  14.1.1. 文件读取示例
14.2. select 循环
14.3. for 循环
  14.3.1. 示例
15. shell 数组
16. shell 调试
16.1. 全局调试
16.2. 分段调试
17. linux 信号
17.1. 常用信号
17.2. trap 命令
18. Advanced Bash-Scripting Guide(Contributed Scripts)
19. getops
20. 那些很神奇的操作


1. shell 脚本执行的几种方法

  • bash(sh) /path/script.sh
  • 赋予执行权限 /path/script.sh./scripts.sh
  • source script.sh. script.sh
    • 在此方法中执行,子shell中定义的变量,可在父shell中调用(其他方式父shell不能直接调用子shell的变量)
  • bash(sh) < script.shcat script.sh | bash(sh)

2. shell 脚本规范

  1. #!/bin/bash#!/bin/sh 开头
  2. 注释标注, 作者、联系方式、时间、版本、脚本描述
  3. 脚本尽量不是使用中文注释
  4. 脚本以.sh 为扩展名
  5. 成对书写符号、条件控制语句等。如: []{}if []; then fi

3. 变量的设置与取消

  • 设置 : 变量名=值
    • 变量名一般大写
  • 打印 : echo $变量名
  • 取消 : unset 变量名

4. 变量定义

4.1. 普通变量定义

  • 变量名=value
  • 变量名='value'
  • 变量名="value"
  • 变量名=$(ls)
  • 变量名=`ls`

4.2. 变量名定义要求

变量名一般由字母、数字、下划线组成

4.3. 示例

  • a=123
  • b=123-$a ## 当值没有单(双)引号的时候,变量的值为123-变量a的值,若变量值出现空格,则值为第一个空格之前的数据
  • c='123-$a' ## 当值存在单引号的时候,值为什么,打印结果则为什么,引号内内容视为一个整体
  • d="123-$a" ## 当值为双引号的时候,若值中存在变量、命令(需要转义)等,会优先把变量、命令结果输出,在打印所有值

5. 特殊、内置变量

变量名 说明
$0 获取当前执行脚本的名称,如果执行脚本带有路径,则包含脚本路径
$n 获取当前执行脚本的第n个参数,n=1-9,n为0时,表示脚本文件名,如果n大于9,用大括号括起来${10},参数以空格隔开
$* 获取当前脚本所有传入的参数,将所有的参数视为单个字符串,相当于"$1$2$3"...
$# 获取当前脚本传入参数的个数总数
$@ 获取当前脚本所有传入参数,将所有的参数分别传入至其他变量或脚本(获取脚本最后一个参数:${@: -1}
$? 确定上一个指令的返回值,0成功, 非0不成功
2: 权限拒绝 ;
1-125: 运行失败,参数传递错误 ;
126: 找到该命令,但无法执行 ;
127:未找到运行的命令 ;
128: 命令被强行中断 ;
脚本中一般用exit 0 , 在执行脚本,返回值给$?, 函数中一般用return 返回值给$?
$$ 当前脚本执行的进程号

$*$@ 的示例:

[root@00 ~]# set -- hello my "linux shell" [root@00 ~]# echo $# 3 [root@00 ~]# echo $1 hello [root@00 ~]# echo $2 my [root@00 ~]# echo $3 linux shell [root@00 ~]# for i in "$@"; do echo $i;done hello my linux shell [root@00 ~]# for i in "$*"; do echo $i;done hello my linux shell

6. 常用操作表达式

表达式 说明
${#string} 返回$string的长度
${string:position} $stirng中,从$position之后开始提取子串
${string:position:length} $string,从位置$position之后开始提取长度为$length的子串
${string#substring} 从变量$string开头开始删除最短匹配$substring的子串
${string##substring} 从变量$string开头开始删除最长匹配$substring的子串
${string%substring} 从变量$string结尾开始删除最短匹配$substring的子串
${string%%substring} 从变量$string结尾开始删除最长匹配$substring的子串
${string/pattern/parameter} 在变量string中,使用parameter替换pattern匹配的第一个值
${string//pattern/parameter} 在变量string中,使用parameter替换所有pattern匹配的值
${string/#pattern/parameter} 在变量string中,使用parameter替换以pattern开头的值
${string/%pattern/parameter} 在变量string中,使用parameter替换以pattern结尾的值
  • 字符串截取

https://blog.csdn.net/LLZK_/article/details/66972377

7. 变量替换表

运算符号 替换
${value:-word} 如果变量value存在且非null,则返回变量的值,否则,返回word字符串. 例: res=${value:-word},如果value未定义,则res的值为word
${value:=word} 如果变量value存在且非null,则返回变量的值,否则,则设置这个变量值为word. 例: res=${value:=word},如果value未定义,则res的值为word,value值也为word
${value:+word} 如果value存在且非null,则返回word,否则返回null.例res=${value:+word},如果value已经定义, 则res的值为word,如果value值未定义,则res值为null(空)

8. 常见的运算符

8.1. 变量运算符

运算符 说明
++ -- 自增与自减; 符号在前代表先运算在赋值,符号在后代表先赋值在运算 -
+ - ! ~ - -
* / % 乘、除、模 / 取整 ; % 取余
+ - 加、减 -
< <= > >= 小于、小于等于、大于、大于等于 -
== != =~ 等于、不等于、正则匹配符 [[ $VAR =~ ^[a-zA-Z] ]],正则不可用引号括起来,变量可单双引号
<< >> 位运算: 左移、右移 二进制计算
&& 逻辑的 and true && false ,结果 false
|| 逻辑的 or true || false ,结果 true
= += -= *= /= %= 赋值运算 (a+=b) == (a=a+b); 其他同理
** 幕运算 2**3=8

8.2. 算术运算符

运算符
(建议在[]以及test中使用的)
运算符
(建议在(())[[]]中使用的)
说明
-eq === 检测2个数是否相等,相等返回true
-ne != 检测2个数是否不相等,相等返回true
-gt > 检测左边的数是否大于右边的,如果是,则返回true
-ge >= 检测左边的数是否大于等于右边的,如果是,则返回true
-lt < 检测左边的数是否小于右边的,如果是,则返回true
-le <= 检测左边的数是否小于等于右边的,如果是,则返回true

8.3. 关系运算符

运算符
(建议在[]test中使用)
运算符
(建议在[[]]中使用)
说明
! ! 非运算,表达式为true,则返回false,否则返回true; 例: [!false]返回true
-o || 或运算,有一个表达式为true,则返回``true
-a && 与运算,2个表达式都为true,才返回``true

8.4. 条件判断符

判断符(man test) 说明(如果存在为true)
-e 文件存在
-f 被测文件是一个regular文件(正常文件,非目录或设备)
-s 如果文件存在且文件大小大于零,则返回真
-d 被测对象是目录
-b 被测对象是块设备
-c 被测对象是字符设备
-p 被测对象是管道
-h 被测文件是符号连接
-L 被测文件是符号连接
-s 被测文件是一个socket
-t 关联到一个终端设备的文件描述符。用来检测脚本的stdin[-t0][-t1]是一个终端
-r 文件具有读权限,针对运行脚本的用户
-w 文件具有写权限,针对运行脚本的用户
-x 文件具有执行权限,针对运行脚本的用户
-u set-user-id(suid)标志到文件,即普通用户可以使用的root权限文件,通过chmod +s file实现
-k 设置粘贴位
-O 运行脚本的用户是文件的所有者
-G 文件的group-id和运行脚本的用户相同
-N 从文件最后被阅读到现在,是否被修改
-z 字符串为null,即长度为0
-n 字符串不为null,即长度不为0
= 字符串是否相等,可以使用==代替=
!= 字符串是否不相等
f1 -nt f2 文件f1是否比f2新
f1 -ot f2 文件f1是否比f2旧
f1 -ef f2 文件f1和f2是否硬连接到同一个文件

8.5. 标准I/O重定向

重定向操作符 功能
< filename 重定向输入
> filename 重定向输出
>> filename 追加输出
2> filename 重定向标准错误输出
2>> filename 重定向和追加标准错误输出
&> filename 重定向标准输出和标准错误输出
>& filename 重定向标准输出和标准错误输出(首选方式)
2>&1 将标准错误输出重定向到输出的去处
1>&2 将输出重定向到标准错误输出的去处
> 重定向输出时忽略noclobber
<> filename 如果是一个设备文件(/dev),使用巍峨年作为标准输入和标准输出

9. 变量的数值运算

  • (())
    • 代表直接进行运算:,如: echo $((1+1+2))((a=1+1+2)) ; echo $a
  • let
    • 将等式直接进行运算, 如: i=1; i=i+1 ; (echo $i) == i+1, 使用let将等式直接进行计算i=1;let i=i+1; (echo $i) == 2
  • expr
    • 只能用于整数的计算,可用于判断变量是否为整数(运算符号两边必须有空格,特殊符号需要需要转移), 如 (expr 1 + 1) == 2; (expr 1 + 1.1) == (expr: non-integer argument)
    • 计算字符串长度a=123456; expr length $a
  • bc
    • 可用于小数计算,进制之间的转换, 如: (echo 1.1 + 2| bc) == 3.1(echo "obase=2;10"|bc) == 1010
  • $[]
    • 这个和(()) 类似
  • typeset
    • 个人的理解就是定义多个int类型的变量,若定义变量值为非数字,则值将被赋予为0(建议自行测试). 如: a=0;typeset -i b=1 c=a;(echo $b $c) == (1 0)a="bb" ; b="123";typeset -i c=a d=b e=222; (echo $c $d $e) == (0 123 222)

10. 变量读入

10.1. read bash内置变量

-p 设置提示信息; -t 设置输入等待时间(默认s),超过时间自动退出

[root@00 ~]# read -p "hello bash :" num1 num2 # " 和变量之间需要一个空格 hello bash : hello_1 hello_2 [root@00 ~]# echo $num1 $num2 hello_1 hello_2

11. 条件测试与比较

11.1. 方法

  1. test <表达式>,如: test -f /etc/hosts
  2. [ <表达式> ] ,如: [ -f /etc/hosts ]
  3. [[ <表达式> ]],如: [[ -f /etc/hosts ]][[ -d /etc && -f /etc/hosts ]][[ -d /etc -a -f /etc/hosts ]][[ -d /etc || -f /etc/hosts ]][[ -d /etc -o -f /etc/hosts ]]

11.2. 说明

  • 上述一和二是等等价的, 如: test -f /etc/hosts == [ -f /etc/hosts ]
  • [[]][]的区别是在[[]] 中可以使用通配符模式进行匹配(如[[ hello == hell? ]] == true:其匹配字符串或通配符时,可不需要引号,[]也可不需要引号,但据说有时候会出现故障,因此建议加上引号), &&、||、>、<等操作符也可以应用于[[]]中,但不能应用于[]

11.3. 补充

关于()(())[][[]]{}几个的区别,接入一个很详细的文档,但是我表示没怎么看懂,先记录下来,以后慢慢看

https://blog.csdn.net/taiyang1987912/article/details/39551385

12. 函数

12.1. 语法

<函数名>(){ ... return n } function <函数名>(){ ... return n }

12.2. 调用

  1. 直接执行函数名即可
    • 执行函数的时候,不需要带括号
    • 函数定义必须在调用之前, shell执行,从上向下
  2. 带参数调用和脚本传参一样
  3. 在函数中,$0 仍然是脚本名称 , $1..n代表是脚本参数
  4. 在函数中使用exit会退出整个脚本,return是跳出当前函数
  5. 函数脚本引用
    • 脚本内加载. /path/scrpt.sh,相当于将/path/script.sh内容直接加载到当前脚本中

13. 判断分支

case $VAR in "1"|a) echo 1 ;; 2|3) echo 2 or 3 ;; *) echo default ;; esac if [ $VAR == $VAR ]; then ... fi

14. 循环

14.1. while 循环

# 满足条件进入(先判断,在执行) while True ; do ... done # 先执行一次,在判断 until True ; do ... done

14.1.1. 文件读取示例

# 示例1(文件最后一行内容需要换行符,否则将无法读取) exec < file while read line; do echo $line done # 示例2 (文件最后一行内容需要换行符,否则将无法读取) cat FILE | while read line ; do echo $line done # 示例3,此方法根据评测据说效率最高 (文件最后一行内容需要换行符,否则将无法读取) while read line ; do ... done < FILE

14.2. select 循环

select 可用于选择包含多个选项的菜单

# set shuttle list PS3="请选择要操作的编号 : " select shuttle in columbia endeavour challenger discovery atlantis enterprise pathfinder; do echo "$REPLY. $shuttle selected" done

14.3. for 循环

# foreach for i in <...> ; do ... done # for for ((<..>;<..>; <..> )); do ... done

14.3.1. 示例

for i in {1..100}; do echo $i done for (( i = 0; i < 100; i ++ )); do echo $i done # echo {1..100}+ |sed 's#\+$##g'|bc

15. shell 数组

  1. 定义 array=(v1 v2 v3)
    • { array=(v1 v2 v3) } == { declare -A array=([0]=v1 [1]=v2 [2]=v3) }
  2. 长度 len=${#array[@]} 或者 len=${#array[*]}
  3. 打印 echo ${array[0]}echo ${array[1]} ...
  4. 变量列表 ${array[*]}${array[@]}
  5. 增、删、改 array[3]=v4unset array[0]array[0]=v5
    • 增加 :(整型数组) 在数组增加时候,需要指定下标,增加到那个下标下面,则取值的时候就应该从那个下标取定义的值(如果要深入使用,此处一定要自行测试,他与其他语言数组有一定区别,个人理解,要说用其他语言来形容的话,他就相当于只有map或多维map的概念)
    • 打印数组下标 echo ${!array[@]}

16. shell 调试

16.1. 全局调试

  • -n 不会执行脚本,仅仅检查语法是否有问题,并给出错误提示
  • -v 在执行脚本是时候,先将脚本的内容输出到屏幕上,然后执行脚本,也会提示错误信息,相当于先cat了
  • -x 将执行的脚本内容即输出显示到屏幕上面

16.2. 分段调试

  • 脚本内 :
#!/bin/bash for i in {1..20} ; do if [ $i -gt 10 ]; then set -x # <<====== 分段调试开始 echo "i>20: $i" set +x # <<====== 分段调试结束 else echo "i<=10: $i" fi done
  • 终端内
[root@00 ~]# set -x # <<==== 调试启动 [root@00 ~]# for i in 1 2 3 ; do echo $i ; done + for i in 1 2 3 + echo 1 1 + for i in 1 2 3 + echo 2 2 + for i in 1 2 3 + echo 3 3 [root@00 ~]# set +x # <<==== 调试结束

17. linux 信号

17.1. 常用信号

信号 - 说明(stty -a查看键盘按键对应的信号)
1 HUP|SIGHUP 挂起,通常因终端雕像或用户退出引发
2 INT|SIGINT 中断,通常是按下Ctrl+C引发
3 QUIT|SIGQUIT 退出,通常是按下Ctrl+/引发
6 ABRT|SIGABRT 终止,通常因为严重的执行错误而引发
9 SIGKILL 立即终止进程
14 ALRM|SIGALRM 报警,通常用来处理超时
15 TERM|SIGTERM 终止,通常在系统挂机时发送
20 TSTP|SIGSTP 停止进程的运行,但该型号可以被处理和忽略,用户键入SUSP字符(通常是Ctrl+z)发出这个信号

17.2. trap 命令

trap 命令用于在接受到信号后将要采取的行动,常用于脚本程序中断时完成清理工作,可用于跳板机脚本制作(由于脚本从上向下进行执行,因此需要将在要保护的程序片段之前进行trap设置)
语法:

  • trap -l 打印所有信号
  • trap -p 打印当前trap设置

示例:

  • trap "" signals 为空表示这个信号失效
  • trap "commands" signals 表示收到signals信号时,信号功能副为同时执行commands命令
  • trap signals 信号复原,取消已经设置的信号
# 临时生效,终端退出失效 [root@00 ~]# trap "" 2 # 设置信号 [root@00 ~]# trap -p # 打印设置信号 trap -- '' SIGINT [root@00 ~]# # << 此时按Ctrl+c 无任何反映 [root@00 ~]# trap 2 # 信号复原 [root@00 ~]# ^C # 复员后 Ctrl+c [root@00 ~]# ^C # 复员后 Ctrl+c

18. Advanced Bash-Scripting Guide(Contributed Scripts)

http://tldp.org/LDP/abs/html/contributed-scripts.html

19. getops

# 双冒号 双冒号短参数必须贴近或无参数,长参数必须等号赋值(长参数名可以不用写完)或无参数(注:无参数时变量偏移也是2位) # 单冒号参数可以贴近也可以不贴近,但参数必选 # f d a 必须接受参数 # s 参数可选 # ARGS=`getopt -o f:s::d:a: --long filename:,source::,desc:,action:: -- "$@"` eval set -- "$ARGS" while true ; do case "$1" in -f|--filename) fileName=$2 ; shift 2 ;; -s|--source) case "$2" in "") sourceDir='.' ; shift 2 ;; *) sourceDir=$2 ; shift 2 ;; esac ;; -d|--desc) descDir=$2 ; shift 2;; -a|--action) case "$2" in "copy"|"move") action=$2 ; shift 2 ;; *) action="copy" ; shift 2 ;; esac ;; --) shift ; break ;; *) echo "Internal error!" ; exit 1 ;; esac done

20. 那些很神奇的操作

  • shellscript 中转义 !
    #!/bin/bash echo -e "\0041" echo -e "#\0041/bin/bash"