1.3 shell 脚本编程

Unix shell

shell 顾名思义为“壳”,主要是相对于系统内核来说的,可以理解为内核的“壳”,shell 负责将用户的指令解释并传给内核执行,直接接触内核是不被允许并危险的,所以 shell 就是一种命令解释器,常见的 shell 有很多,分为图形界面 shell 和命令行 shell,命令行中最常用的就是 bash shell。

系统中,通过修改.bashrc文件来配置使用环境,输入source ~/.bashrc立刻使配置文件生效,如要配置全局生效,可修改/etc/bashrc 文件。

通过 cat /etc/shells 查看当前启用的 shell;通过 echo $SHELL查看用户的默认登录 shell。

Bash shell

在 Bash 中,可以通过 Tab补全命令。

使用history 命令查看1000条命令行历史记录,再用 !nn 是命令编号)就可以再次执行该命令。

使用ctrl-r搜索命令行历史记录:按下按键之后,输入关键字便可以搜索,重复按下ctrl-r会向后查找匹配项,按下Enter键会执行当前匹配的命令,而按下右方向键会将匹配项放入当前行中,不会直接执行,以便做出修改。

长文本编辑技巧:键入ctrl-A可以将光标移至行首;键入ctrl-E可以将光标移至行尾。键入Ctrl-K​删除从光标到行尾​的内容。

使用cd命令可以切换工作路径,~ 表示用户家目录,要访问家目录中的文件,可以使用前缀 ~,例如 vi ~/.bashrc。在脚本里则用环境变量 $HOME 指代家目录。

在两个工作目录之前来回切换是件很恼人的事情,可以使用cd -命令来快速在二者之前切换。

Vim 编辑器

当程序步骤复杂时,bash 有其局限性,所以要求用户熟悉至少一种基于文本的编辑器,编辑器可以将你的命令按顺序执行,称为脚本。通常 Vim 会是最好的选择(除了 vim 还有 Emacs 等编辑器)。

Vim 是从 vi 发展出来的一个文本编辑器。代码补全、错误跳转等功能特别丰富,在程序员中被广泛使用。vi/vim 分为三种模式,分别是命令模式(Command mode)输入/插入模式(Insert mode)底线命令/命令行模式(Last line mode)。 这三种模式的作用分别是:

命令模式:

用户启动vi/vim,就进入了命令模式。

此状态下敲击键盘动作会被Vim识别为命令,而非输入字符。命令模式只有一些最基本的命令(复制粘贴等等),因此通常需要底线命令模式扩展其功能。

输入模式/插入模式:

在命令模式下按i键,进入输入模式。

按下键盘ESC键,退出输入模式,切换到命令模式。完成文件的编辑后,在命令行模式中输入wq存盘退出,也可使用ZZ来达到同样的效果。

底线命令模式:

在命令模式下按下:(英文冒号),进入底线命令模式。

底线命令模式可以输入单个或多个字符的命令,可用的命令非常多。按ESC键退出底线命令模式。

数据块模式:

使用control+v进入数据块选中模式,I进入插入模式,ESC后会统一在数据块区域插入相同内容。

在 Vim 中输入:help user-manual进入用户手册。也有手册中文版可供参考。下面介绍了一些不同模式下的常用命令:

在Vim中完成一个简单的脚本:

使用vim来建立一个名为test.sh的文件:

并且在插入模式中写出脚本后存盘退出。

终端中输入./test.ah运行这个脚本。

echo命令 用于在shell中打印shell变量的值,或者直接输出指定的字符串。Linux 的echo命令,在 shell 编程中极为常用, 在终端下打印变量value的时候也是常常用到的。当echo使用-e参数时,允许使用转译字符,如/t是插入一个制表符。

文件恢复

在编辑文件的过程中,Vim 将会在当前目录中自动生成一个以.swp结尾的临时交换文件,用于备份缓冲区中的内容,以便在意外退出时可以恢复之前编辑的内容。

完成编辑并保存退出后,临时交换文件会被删除;但如果 Vim 意外退出,那么这个临时文件就会留在硬盘中。当 Vim 再次启动时,会检查当前目录中是否存在交换文件。如果存在,则意味着 Vim 正在编辑此文件(他人正在编辑),或者在上次编辑过程中意外退出,这时 Vim 就会给出警告信息,并要求我们在以下四个选项中做出选择:

  • Open Read-Only(以只读方式打开):如果我们想要查看文件内容或是有另一个编辑过程正在运行,那么可以选择此选项;

  • Edit anyway(编辑文件):请尽量不要选择此选项。因为如果同时有两个或是多个编辑过程同时编辑一个文件,那么只有最后一个保存的编辑过程有效;

  • Recover(恢复):如果在编辑过程中vim意外退出,那么可以选择此选项尝试从交换文件恢复文档;

  • Quit(退出):选择此选项,将取消对此文件的修改。

Vim 意外退出后,使用-r参数直接恢复编辑。如vi -r filename

Help poor children in Uganda !

vim 是一款开源免费软件,不会向用户索取任何费用。但会建议用户向荷兰 ICCF 捐款,用于帮助乌干达的艾滋病患者:vim 的启动页会显示 「 Help poor children in Uganda! 」 的字样,中文版本中则是 「 请帮助乌干达的可怜孩童!」。

这是因为 vim 的作者 Bram Moolenaar 去过乌干达开展志愿服务,在那里他了解了当地儿童的极端生存状况,知道他们需要帮助,于是 Bram Moolenaar 回国后与其他人一起创立了 ICCF 基金会。通过 vim 捐赠的善款将全部用于帮助改善乌干达的儿童的饮食、医疗、教育等状况。

在命令行模式中输入 help uganda可以看到详细的捐款方式。

变量用法与规定

假设执行 ./test.sh a b c 这样一个命令,则可以使用下面的值来获取对应参数:

  • $0对应 "./test.sh" 这个值。如果执行的是 ./work/test.sh, 则对应 ./work/test.sh 这个值,而不是只返回文件名本身的部分(脚本中则代指所有域)

  • $1会获取到 a,即$1对应传给脚本的第一个参数

  • $2会获取到 b,即 $2 对应传给脚本的第二个参数

  • $3会获取到 c,即 $3 对应传给脚本的第三个参数。$4、$5 等参数的含义依此类推

  • $#会获取到 3,对应参数个数,统计的参数不包括$0

  • $@会获取到"a" "b" "c",也就是所有参数的列表,不包括$0

  • $*也会获取到"a" "b" "c", 其值和$@相同。但 "$*" 和 "$@" 有所不同。$*把所有参数合并成一个字符串,而 "$@" 会得到一个字符串参数数组

  • $?可以获取到执行./test.sh a b c命令后的返回值。在执行一个前台命令后,可以立即用 $?获取到该命令的返回值。该命令可以是系统自身的命令,可以是 shell 脚本,也可以是自定义的 bash 函数。

输入与输出 I/O

逻辑与判断

数值与逻辑判断语法

if 语句

迭代与循环

e.g.

错误用法 [0 -eq 1] ;正确用法 [ 0 -eq 1 ]

命令的传递

符号
作用

|

管道符:将前一个命令的输出作为第二个命令的参数。

||

逻辑或:前一个命令执行错误后第二个命令才会执行,否则第一个命令不执行。

&&

逻辑与:当前一个命令正确执行后才会执行第二个命令。

;

多命令顺序执行,命令之间无任何逻辑关系。

不是所有的命令都支持管道符,如ls类命令 (但可以配合xargs的命令使用)

e.g. 将cat的输出结果作为grep的参数,只打印出包含“alias”的行

e.g. 若 lab1 不存在,则创建 lab1

e.g. 若存在 lab1 ,则删除 lab1

e.g. 使用分号将编译命令和运行命令连接在一起,以便在编译成功后立即运行程序

Linux 文本三剑客

重定向符号

输入重定向符号

符号
作用

命令 < 文件

将文件作为命令的标准输入

命令 << 分界符

从标准输入中读入,直到遇到分界符停止

命令 < 文件1 >文件2

将文件1作为命令的标准输入并将标准输出到文件2

输出重定向符号

符号
作用

命令 > 文件

将标准输出重定向到文件中(清除原有文件中的数据)

命令 2> 文件

将错误输出重定向到文件中(清除原有文件中的数据)

命令 >> 文件

将标准输出重定向到文件中(在原有的内容后追加)

命令 2>> 文件

将错误输出重定向到文件中(在原有内容后面追加)

命令 >> 文件 2>&1

命令 &>> 文件

将标准输出和错误输出共同写入文件(在原有内容后追加)

awk 命令

数据可以来自标准输入,一个或多个文件。支持正则表达

格式:

e.g.

脚本通常由''""包含:

语句块通常由{}包含:

有无空格不影响脚本执行,但如果需要打印空格,可以使用" "包裹起来:

指定某一位的字符,真则打印:

NR 表示记录数,执行过程中对应行号

语句块 BEGIN{ commands } pattern{ commands } END{ commands }

  • nlines 行数

BEGIN 语句块 在 awk 开始从输入流中读取行 之前 被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表>头等语句通常可以写在 BEGIN 语句块中

END 语句块 在 awk 从输入流中读取完所有的行 之后 即被执行,比如打印所有行的分析结果这类信息汇总都是在 END 语句块>中完成,它也是一个可选语句块

pattern 语句块 中的通用命令是最重要的部分,它也是可选的。如果没有提供 pattern 语句块,则默认执行 { print } >,即打印每一个读取到的行,awk 读取的每 一行都会执行该语句块

e.g. 仅复制文件

sed 命令

stream editor 流编辑器,支持正则表达,功能非同凡响。处理模式为逐行读入输入文件,并通过执行 sed 命令或者脚本的操作,然后处理后的结果输出到标准输出。

格式

指定显示

替换删除

指定插入

匹配标记

组合表达

正则表达

引用

如果表达式内包含字符串变量,则需要用双引号:

grep 命令

可以抓取你想要的字符,grep 命令可以抓取字符所在行的文本。常用的参数有:

-i忽略大小写进行匹配;

-v反向查找,只打印不匹配的行;

-r递归查找。可以同时打印文件名和所在行内容,这在使用通配符查找时很有用。

最后更新于