1. 什么是shell

1075188-20191221111411013-65042932.png

在计算机科学中,Shell俗称(用来区别于核),是指“为使用者提供操作界面”的软件(命令解析器)。它类似于DOS下的command.com和后来的cmd.exe。它接收用户命令,然后调用相应的应用程序。
同时它又是一种程序设计语言。作为命令语言,它交互式解释和执行用户输入的命令或者自动地解释和执行预先设定好的一连串的命令;作为程序设计语言,它定义了各种变量和参数,并提供了许多在高级语言中才具有的控制结构,包括循环和分支。
shell分GUI shell、CLI shell两种。
GUI shell 顾名思义就是图形界面的shell,比如windows下的Explorer、linux下的X windows manager以及功能更为强大的GNOME、KDE等。
CLI shell 命令行式的shell,比如windows下的cmd.exe/powershell、linux下的bash、ksh、zsh等。
传统意义上的shell指的是命令行式的shell,以后如果不特别注明,shell是指命令行式的shell,这里主要为linux下的bash。


Shell 脚本(shell script),是一种为 shell 编写的脚本程序。
业界所说的 shell 通常都是指 shell 脚本,但实际上来说,shell 和 shell script 是两个不同的概念。
由于习惯的原因,简洁起见,本文出现的 “shell编程” 都是指 shell 脚本编程,不是指开发 shell 自身。

2. shell分类

Shell 编程跟 JavaScript、php 编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。
Linux 的 Shell 种类众多,常见的有:

  • Bourne Shell(/usr/bin/sh或/bin/sh)
  • Bourne Again Shell(/bin/bash)
  • C Shell(/usr/bin/csh)
  • K Shell(/usr/bin/ksh)
  • Shell for Root(/sbin/sh)
  • ……

    3. shell的基本用法

    1. 第一个shell脚本

  • 简单样例

使用任何一个文本编辑器(vi/vim或者其他常用工具),新建一个文件test.sh,扩展名为sh,以见名知意。

  1. #!/bin/bash
  2. echo "Hello World !"

#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell。
echo 命令用于向窗口输出文本。

  • 脚本运行的两种方式
    1. 作为可执行程序
      1. chmod +x ./test.sh #使脚本具有执行权限
      2. ./test.sh #执行脚本

需要sh脚本文件有执行权限,没有的话,使用chmod +x ./test.sh增加权限

  1. 1. **作为解释器参数**
  1. /bin/sh test.sh

这种方式运行的脚本,不需要在第一行指定解释器信息,写了也没用。

2. 变量名

3. 参数

参数处理 说明
$0~$n $0代表shell脚本名称,$1~$n则为脚本后面的第一个到第n个参数
$# 传递到脚本的参数个数
$* 以一个单字符串显示所有向脚本传递的参数。
如”$*”用「”」括起来的情况、以”$1 $2 … $n”的形式输出所有参数。
$$ 脚本运行的当前进程ID号
$! 后台运行的最后一个进程的ID号
$@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。
如”$@”用「”」括起来的情况、以”$1” “$2” … “$n” 的形式输出所有参数。
$- 显示Shell使用的当前选项,与set命令功能相同。
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

4. 数组

5. shell基本运算

  1. 原生bash是不支持算术运算的。但是可以与其他命令结合使用 比如awksed以及expr。这里以expr最为常用。<br />expr 是一款表达式计算工具,使用它能完成表达式的求值操作。<br />例如,两个数相加(**注意使用的是反引号 ` 而不是单引号 '**):
  1. #!/bin/bash
  2. val=`expr 2 + 2`
  3. echo "两数之和为 : $val"

执行脚本,输出结果如下所示:

  1. 两数之和为 : 4

两点注意:

  • 表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2,这与我们熟悉的大多数编程语言不一样。与shell变量赋值就不一样。shell变量赋值=两端要求不带空格。
  • 完整的表达式要被 包含,注意这个字符不是常用的单引号,在 Esc 键下边。
  • 算数运算符

下表列出了常用的算术运算符,假定变量 a 为 10,变量 b 为 20:

运算符 说明 举例
+ 加法 expr $a + $b 结果为 30。
- 减法 expr $a - $b 结果为 -10。
* 乘法 expr $a \\* $b 结果为 200。
/ 除法 expr $b / $a 结果为 2。
% 取余 expr $b % $a 结果为 0。
= 赋值 a=$b 将把变量 b 的值赋给 a。
== 相等。用于比较两个数字,相同则返回 true。 [ $a == $b ] 返回 false。
!= 不相等。用于比较两个数字,不相同则返回 true。 [ $a != $b ] 返回 true。

注意:条件表达式要放在方括号之间,并且要有空格,例如: [$a==$b] 是错误的,必须写成 [ $a == $b ]

  • 关系运算符

关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
下表列出了常用的关系运算符,假定变量 a 为 10,变量 b 为 20:

运算符 说明 举例
-eq 检测两个数是否相等,相等返回 true。 [ $a -eq $b ] 返回 false。
-ne 检测两个数是否不相等,不相等返回 true。 [ $a -ne $b ] 返回 true。
-gt 检测左边的数是否大于右边的,如果是,则返回 true。 [ $a -gt $b ] 返回 false。
-lt 检测左边的数是否小于右边的,如果是,则返回 true。 [ $a -lt $b ] 返回 true。
-ge 检测左边的数是否大于等于右边的,如果是,则返回 true。 [ $a -ge $b ] 返回 false。
-le 检测左边的数是否小于等于右边的,如果是,则返回 true。 [ $a -le $b ] 返回 true。
  • 布尔运算符

下表列出了常用的布尔运算符,假定变量 a 为 10,变量 b 为 20:

运算符 说明 举例
! 非运算,表达式为 true 则返回 false,否则返回 true。 [ ! false ] 返回 true。
-o 或运算,有一个表达式为 true 则返回 true。 [ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a 与运算,两个表达式都为 true 才返回 true。 [ $a -lt 20 -a $b -gt 100 ] 返回 false。
  • 逻辑运算符

以下介绍 Shell 的逻辑运算符,假定变量 a 为 10,变量 b 为 20:

运算符 说明 举例
&& 逻辑的 AND [[ $a -lt 100 && $b -gt 100 ]] 返回 false
|| 逻辑的 OR [[ $a -lt 100 || $b -gt 100 ]] 返回 true
  • 字符串运算符

下表列出了常用的字符串运算符,假定变量 a 为 “abc”,变量 b 为 “efg”:

运算符 说明 举例
= 检测两个字符串是否相等,相等返回 true。 [ $a = $b ] 返回 false。
!= 检测两个字符串是否相等,不相等返回 true。 [ $a != $b ] 返回 true。
-z 检测字符串长度是否为0,为0返回 true。 [ -z $a ] 返回 false。
-n 检测字符串长度是否为0,不为0返回 true。 [ -n “$a” ] 返回 true。
$ 检测字符串是否为空,不为空返回 true。 [ $a ] 返回 true。
  • 文件测试运算符

文件测试运算符用于检测 Unix 文件的各种属性。
属性检测描述如下:

操作符 说明 举例
-b file 检测文件是否是块设备文件,如果是,则返回 true。 [ -b $file ] 返回 false。
-c file 检测文件是否是字符设备文件,如果是,则返回 true。 [ -c $file ] 返回 false。
-d file 检测文件是否是目录,如果是,则返回 true。 [ -d $file ] 返回 false。
-f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 [ -f $file ] 返回 true。
-g file 检测文件是否设置了 SGID 位,如果是,则返回 true。 [ -g $file ] 返回 false。
-k file 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 [ -k $file ] 返回 false。
-p file 检测文件是否是有名管道,如果是,则返回 true。 [ -p $file ] 返回 false。
-u file 检测文件是否设置了 SUID 位,如果是,则返回 true。 [ -u $file ] 返回 false。
-r file 检测文件是否可读,如果是,则返回 true。 [ -r $file ] 返回 true。
-w file 检测文件是否可写,如果是,则返回 true。 [ -w $file ] 返回 true。
-x file 检测文件是否可执行,如果是,则返回 true。 [ -x $file ] 返回 true。
-s file 检测文件是否为空(文件大小是否大于0),不为空返回 true。 [ -s $file ] 返回 true。
-e file 检测文件(包括目录)是否存在,如果是,则返回 true。 [ -e $file ] 返回 true。

其他检查符:

  • -S: 判断某文件是否 socket。
  • -L: 检测文件是否存在并且是一个符号链接。

    6. echo命令

    Shell 的 echo 指令与 PHP 的 echo 指令类似,都是用于字符串的输出。命令格式:
    echo string
    echo命令有多种使用用法 参考:链接

    7. printf命令

    printf 命令模仿 C 程序库(library)里的 printf() 程序。
    printf 由 POSIX 标准所定义,因此使用 printf 的脚本比使用 echo 移植性好。
    printf 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。默认 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n。
    printf 命令的语法:
    printf format-string [arguments…]
    参数说明:

  • format-string: 为格式控制字符串

  • arguments: 为参数列表。

printf的转义序列

序列 说明
\a 警告字符,通常为ASCII的BEL字符
\b 后退
\c 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
\f 换页(formfeed)
\n 换行
\r 回车(Carriage return)
\t 水平制表符
\v 垂直制表符
\\ 一个字面上的反斜杠字符
\ddd 表示1到3位数八进制值的字符。仅在格式字符串中有效
\0ddd 表示1到3位的八进制值字符

8. test命令

Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。
数值测试

参数 说明
-eq 等于则为真
-ne 不等于则为真
-gt 大于则为真
-ge 大于等于则为真
-lt 小于则为真
-le 小于等于则为真

字符串测试

参数 说明
= 等于则为真
!= 不相等则为真
-z 字符串 字符串的长度为零则为真
-n 字符串 字符串的长度不为零则为真

文件测试

参数 说明
-e 文件名 如果文件存在则为真
-r 文件名 如果文件存在且可读则为真
-w 文件名 如果文件存在且可写则为真
-x 文件名 如果文件存在且可执行则为真
-s 文件名 如果文件存在且至少有一个字符则为真
-d 文件名 如果文件存在且为目录则为真
-f 文件名 如果文件存在且为普通文件则为真
-c 文件名 如果文件存在且为字符型特殊文件则为真
-b 文件名 如果文件存在且为块特殊文件则为真

另外,Shell还提供了与( -a )、或( -o )、非( ! )三个逻辑操作符用于将测试条件连接起来,其优先级为:”!”最高,”-a”次之,”-o”最低。

9. 流程控制

  • if else
  1. #第一种 if
  2. if condition
  3. then
  4. command1
  5. command2
  6. ...
  7. commandN
  8. fi
  9. #第二种 if else
  10. if condition
  11. then
  12. command1
  13. command2
  14. ...
  15. commandN
  16. else
  17. command
  18. fi
  19. #第三种 if else-if else
  20. if condition1
  21. then
  22. command1
  23. elif condition2
  24. then
  25. command2
  26. else
  27. commandN
  28. fi
  • for
  1. for var in item1 item2 ... itemN
  2. do
  3. command1
  4. command2
  5. ...
  6. commandN
  7. done

当变量值在列表里,for循环即执行一次所有命令,使用变量名获取列表中的当前取值。命令可为任何有效的shell命令和语句。in列表可以包含替换、字符串和文件名。
in列表是可选的,如果不用它,for循环使用命令行的位置参数。

  • while
  1. while condition
  2. do
  3. command
  4. done
  • 无限循环
  1. while :
  2. do
  3. command
  4. done
  5. 或者
  6. while true
  7. do
  8. command
  9. done
  10. 或者
  11. for (( ; ; ))
  • until循环
  1. until condition
  2. do
  3. command
  4. done
  • case
  1. case in
  2. 模式1)
  3. command1
  4. command2
  5. ...
  6. commandN
  7. ;;
  8. 模式2
  9. command1
  10. command2
  11. ...
  12. commandN
  13. ;;
  14. esac
  • 跳出循环
  • break命令 允许跳出所有循环(终止执行后面的所有循环)
  • continue命令 只跳出当前循环

    10. 函数

    linux shell可以用户定义函数,然后在shell脚本中随便调用
    shell中函数的定义格式如下: ```shell [ function ] funname [()]

{

  1. action;
  2. [return int;]

}

  1. 说明:
  2. - 1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
  3. - 2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255)
  4. - 3、函数返回值在调用该函数后通过 $? 来获得。
  5. - 4、所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。
  6. - 5、在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数... 注意:$10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。
  7. | 参数处理 | 说明 |
  8. | :--- | :--- |
  9. | $# | 传递到脚本或函数的参数个数 |
  10. | $* | 以一个单字符串显示所有向脚本传递的参数 |
  11. | $$ | 脚本运行的当前进程ID |
  12. | $! | 后台运行的最后一个进程的ID |
  13. | $@ | $*相同,但是使用时加引号,并在引号中返回每个参数。 |
  14. | $- | 显示Shell使用的当前选项,与set命令功能相同。 |
  15. | $? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
  16. <a name="ZEx5G"></a>
  17. ## 11. 输入/输出重定向
  18. 重定向命令列表如下:
  19. | 命令 | 说明 |
  20. | :--- | :--- |
  21. | command > file | 将输出重定向到 file |
  22. | command < file | 将输入重定向到 file |
  23. | command >> file | 将输出以追加的方式重定向到 file |
  24. | n > file | 将文件描述符为 n 的文件重定向到 file |
  25. | n >> file | 将文件描述符为 n 的文件以追加的方式重定向到 file |
  26. | n >& m | 将输出文件 m n 合并。 |
  27. | n <& m | 将输入文件 m n 合并。 |
  28. | << tag | 将开始标记 tag 和结束标记 tag 之间的内容作为输入。 |
  29. > 需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
  30. **/dev/null **是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。
  31. <a name="UdfWr"></a>
  32. ## 12. 文件包含
  33. 和其他语言一样,shell也可以包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。<br />shell文件包含的语法格式如下:
  34. ```shell
  35. . filename # 注意点号(.)和文件名中间有一空格
  36. source filename

注:被包含的文件filename 不需要可执行权限。

4. shell的高级用法

1. expr

2. cut

3. sed

3. awk

5. shell的常用脚本

6. 相关书籍和视频

📚书籍

7. 问题

  • shell中[]的用途,什么情况使用?
  • 其他

1 在线shell工具
2 shell内置命令