LV02-02-shell-07-sed命令的使用
本文主要是shell——sed命令的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
Windows | windows11 |
Ubuntu | Ubuntu16.04的64位版本 |
VMware® Workstation 16 Pro | 16.2.3 build-19376536 |
SecureCRT | Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日 |
开发板 | 正点原子 i.MX6ULL Linux阿尔法开发板 |
uboot | NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03) |
linux内核 | linux-4.15(NXP官方提供) |
STM32开发板 | 正点原子战舰V3(STM32F103ZET6) |
点击查看本文参考资料
参考方向 | 参考原文 |
--- | --- |
一、sed 简介
sed 算是是一种在线编辑器,采用的是流编辑模式,它一次处理文件中一行的内容。进行文本处理时,会把当前处理的行存储在临时缓冲区中,称为“模式空间”( pattern space ),接着用 sed 命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕,所以每处理一次就会有一次输出。接着处理下一行,这样不断重复,直到文件末尾。
【注意】 sed 默认不会直接修改源文件数据,而是会将数据复制到缓冲区中,修改也仅限于缓冲区中的数据。
二、 sed 命令
sed 命令,不管是否找到指定的模式,它的退出状态都是 0 。只有当命令存在语法错误时, sed 的退出状态才不是 0 。语法格式如下:
1 | sed [ option ] [ action ] file_name |
- option
option | -n | 该选项会屏蔽启动输出,仅显示script处理后的结果 |
-i | 直接修改文件内容,而不是输出到终端。 | |
-e [script] | 以选项中指定的script来处理输入的文本文件,若之前已有命令,此时会将script脚本命令添加到已有的命令中。 | |
-f [script文件] | 以选项中指定的script文件来处理输入的文本文件,若之前已有命令,此时会将script文件中的脚本命令添加到已有的命令中。 |
【注意】 -i 选项要慎用,毕竟会直接修改原文件。
- action
action | a | 新增, a 的后面可以接字符串,这些字串会在新的一行出现(当前行的下一行)。 【注意】若要插入多行,则行之间要加上 \ 。 |
c | 替换整行,用此符号后的新文本替换当前行中的文本。 | |
d | 删除,通常不接任何东西 | |
i | 插入,后面可以跟字符串,在当前行的上一行插入文本 【注意】若要插入多行,则行之间要加上 \ 。 |
|
p | 打印,通常与 -n 一起使用。 | |
s | 替换字符串,可以搭配正则表达式,用一个字符串替换另一个字符串 | |
y | 将字符替换为另一字符(不能对正则表达式使用y命令) | |
r | 将一个独立文件的数据插入到当前数据流的指定位置 |
【注意】 sed 后面接的动作,要以 ‘ ‘ 两个单引号括住。
- 相关正则表达式
字符 | 说明 | 实例 |
^ | 行首定位符 | /^a/ 匹配所有以a开头的行。 |
$ | 行尾定位符 | /a$/ 匹配所有以 a 结尾的行。 |
. | 匹配除换行符以外的单个字符 | /a..b/ 匹配包含字母 a ,后跟两个任意字符,再跟字母 b 的行。 |
* | 匹配除换行符以外的单个字符 | /a..b/ 匹配包含字母 a ,后跟两个任意字符,再跟字母 b 的行 |
[] | 匹配指定字符组内的任一字符 | /[Aa]b/ 匹配包含 Ab 或 ab 的行。 |
[^] | 匹配不在指定字符组内的任一字符 | /[^Aa]b/ 匹配包含 b ,但 b 之前的那个字符不是 A 或 a 的行。 |
\(..\) | 保存已匹配的字符 | 1,5s/\(you\)self/\1r/ 标记元字符之间的模式,并将其保存为标签 1 ,之后可以使用 \1 来引用它。最多可以定义 9 个标签,从左边开始编号,最左边的是第一个。此例中,对第 1 到第 5 行进行处理,you被保存为标签 1 ,如果发现 youself ,则替换为 your 。 |
& | 保存查找的字符串以便在替换串中引用 | s/ab/**&**/ 符号 & 代表查找的字符串,ab 将会被替换为**ab**。 |
\< | 词首定位符 | /\<ab/ 匹配包含以 ab 开头的单词的行。 |
\> | 词尾定位符 | /ab\>/ 匹配包含以 ab 结尾的单词的行。 |
a\{n\} | 连续 n 个 a | /a\{5\}/ 匹配包含连续 5 个 a 的行。 |
a\{n,\} | 至少连续 n 个 a | /a\{5,\}/ 匹配包含至少连续 5 个 a 的行。 |
a\{m,n\} | 至少连续 m 个,但不超过连续 n 个 a | /a\{5,7\}/ 匹配包含连续 5 到 7 个 a 的行。 |
三、sed 使用实例
只说参数并不能很好理解如何使用,这里将会有大量的实例来帮忙理解。先写一个测试用的文件,就以 Hexo 的站点配置文件为例,删除一些东西,留下一些用于测试使用。文件内容如下:
1 | [1] title: Hexo |
【说明】我这里用的是 Windows 下 Git 的 bash ,与 linux 中应该是一样的。另外运行脚本中的 $ ./1.sh 表示在命令行运行该脚本文件,下边的为输出结果,文件中前边方括号里边的内容为这一行在源文件的行号。
1. a 和 i 动作
1.1 在第 n 行前插入一行
1 | !/bin/sh |
运行结果如下:
原文件 | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
[1] title: Hexo 这是第2行前新插入的行 [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
1.2 在第 n 行后插入一行
1 | !/bin/sh |
运行结果如下:
原文件 | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
[1] title: Hexo [2] author: John Doe 这是第2行后新插入的行 [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
1.3 在最后一行前插入一行
1 | !/bin/sh |
运行结果如下:
原文件 | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape 这是最后一行行前新插入的行 [11]Theme: def |
1.4 在最后一行后插入一行
1 | !/bin/sh |
运行结果如下:
原文件 | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def 这是最后一行行后新插入的行 |
2. d 动作
2.1 通过行号指定删除行
1 | !/bin/sh |
运行结果如下:
原文件 | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
[1] title: Hexo [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
2.2 通过区间删除行
1 | !/bin/sh |
运行结果如下:
原文件 | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
[1] title: Hexo [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
2.3 通过数据搜索删除行
1 | !/bin/sh |
运行结果如下:
原文件 | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [11]Theme: def |
3. c 动作
3.1 通过行号指定替换行
1 | !/bin/sh |
运行结果如下:
原文件 | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
[1] title: Hexo 这是替换后的第2行 [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
3.2 通过区间指定替换行
1 | !/bin/sh |
运行结果如下:
原文件 | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
[1] title: Hexo 2,3行已被替换,这是替换后的行 [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
4. -n选项与p动作
4.1 数据的搜索显示
1 | !/bin/sh |
运行结果如下:
原文件 | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [7] theme: theme: landscape theme [8] theme: aaa [8] theme: aaa [9] theme: landscape [9] theme: landscape [10]theme: landscape [10]theme: landscape [11]Theme: def |
4.2 加上 -n ?
我们会发现上边的输出中,含有 theme 的行每行都被显示了两次,一次是处理的时候的输出,另一次是 p 动作的输出。那么如何只输出我所需要的行呢?
1 | !/bin/sh |
运行结果如下:
原文件 | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
[7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape |
5. s 动作
5.1 使用格式
s 就是字符串替换的命令,使用格式如下:
1 | s/original/target/flags |
original | 需要替换的字符串,也就是原来的字符串 | |
target | 替换后的字符串,也就是目标字符串 | |
flags | n | 1 ~ 512 之间的数字,表示指定要替换的字符串出现第几次时才进行替换,例如,一行中有 5 个 a,但用户只想替换第 3 个 a,就可以使用此标记。 |
g | 对数据中所有匹配到的内容进行替换,如果没有 g,则只会在第 1 次匹配成功时做替换操作。 | |
p | 会打印与替换命令中指定的模式匹配的行。此标记通常与 -n 选项一起使用。 | |
w file | 将缓冲区中的内容写到指定的 file 文件中。 | |
& | 用正则表达式匹配的内容进行替换。 | |
\n | 匹配第 n 个子串,该子串之前在 original 中用 \(\) 指定。 | |
\ | 转义(转义替换部分包含:&、\ 等)。 |
5.2 实例1:替换字符串的 n 与 g 标识
1 | !/bin/sh |
运行结果如下:
原文件 | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: 我是替换的字符串: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
【说明】 -g 选项则是替换所有的匹配值,这里不再说明。
5.3 实例2:显示替换行的 p 标识
1 | !/bin/sh |
运行结果如下:
原文件 | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
[7] 我是替换的字符串: theme: landscape theme [8] 我是替换的字符串: aaa [9] 我是替换的字符串: landscape [10]我是替换的字符串: landscape |
【说明】此时仅仅输出了替换的行,但是,只要含有 theme 的行,每行的第一个 theme 都会被替换。
5.4 实例3:匹配结果进行保存的 w file 标识
1 | !/bin/sh |
运行结果如下:
原文件 | text.txt文件内容(屏幕不显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
[7] 我是替换的字符串: theme: landscape theme [8] 我是替换的字符串: aaa [9] 我是替换的字符串: landscape [10]我是替换的字符串: landscape |
5.5 实例4:转义标识 \
主要是用于替换文件中的路径时用的较多。
1 | !/bin/sh |
运行结果如下:
原文件(这里增加了第12行用于测试) | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def [12]/bin/bash |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def [12]/abs/df |
5.5 实例5:数据搜索并显示并执行命令
1 | !/bin/sh |
运行结果如下:
原文件 | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
[7] theme: theme: next theme |
【说明】该命令会找到 string 出现的第一行,再执行后面花括号中的一组命令,每个命令之间用分号分隔,这里把 landscape 替换为 next ,p 表示显示处理结果, q 表示退出。
6. y动作
6.1 使用格式
y 转换命令是唯一可以处理单个字符的 sed 脚本命令。
1 | [address]y/inchars/outchars/ |
转换命令会对 inchars 和 outchars 值进行一对一的映射,即 inchars 中的第一个字符会被转换为 outchars 中的第一个字符,第二个字符会被转换成 outchars 中的第二个字符…,这个映射过程会一直持续到处理完指定字符。如果 inchars 和 outchars 的长度不同,则 sed 会产生一条错误消息。
6.2 使用实例
1 | !/bin/sh |
运行结果如下:
原文件 | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
[1] TiTlE* HExo [2] auTHor* JoHn DoE [3] languagE* En [4] url* HTTp*//ExaMplE.coM [5] pErMalink* *yEar/*MonTH/*day/*TiTlE/ [6] [7] THEME* THEME* landscapE THEME [8] THEME* aaa [9] THEME* landscapE [10]THEME* landscapE [11]THEME* dEf |
7. r 动作
创建一个 test.txt 的文件,里边写入数据 theme: next 。
1 | !/bin/sh |
运行结果如下:
原文件 | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
[1] title: Hexo [2] author: John Doe [3] language: en theme: next [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
8. -e 选项
- 多条命令连接
1 | !/bin/sh |
运行结果如下:
原文件 | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
[1] title: Hexo [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] string: theme: landscape theme [8] string: aaa [9] string: landscape [10]string: landscape [11]Theme: def |
【说明】删除第 2,3 行,然后将文件中的 theme 用 string 替换。
9. -i 选项(慎用)
- 直接修改文件
1 | !/bin/sh |
运行结果如下:
原文件 | 修改后的文件(屏幕无显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: next theme [8] theme: aaa [9] theme: next [10]theme: next [11]Theme: def |
【说明】可以看到,命令直接替换了所有的字符串。
10. -f 选项
- sed 脚本
sed 脚本就是写在文件中的一列 sed 命令。脚本中,命令的末尾不能有任何多余的空格或文本。如果在一行中有多个命令,要用分号分隔。使用 sed 脚本时,不再用引号来确保 sed 命令不被 shell 解释。
执行脚本时, sed 先将输入文件中第一行复制到模式缓冲区,然后对其执行脚本中所有的命令。每一行处理完毕后, sed 再复制文件中下一行到模式缓冲区,对其执行脚本中所有命令。
四、使用变量?
我们可以在sed命令中使用变量吗?这样我们的脚本的通用性不就很高了吗。我们这里用实例来说明
1. 命令用双引号
第一种方式就是sed后边的命令用双引号括起来,我们可以直接引用变量,需要注意的是在变量中若有路径也要注意转义一下,例如:
1 | !/bin/sh |
运行结果如下:
原文件(这里增加了第12行用于测试) | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def [12]/bin/bash |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def [12]ab/cd/ef/gh |
2. 命令用单引号
第二种就是命令若是单引号的话,引用的变量也要单引号括起来,需要注意的是在变量中若有路径也要注意转义一下,例如:
1 | !/bin/sh |
运行结果如下:
原文件(这里增加了第12行用于测试) | 缓冲区输出(屏幕显示) |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def [12]/bin/bash |
[1] title: Hexo [2] author: John Doe [3] language: en [4] url: http://example.com [5] permalink: :year/:month/:day/:title/ [6] [7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape [11]Theme: def [12]ab/cd/ef/gh |
五、空文件不生效?
后来在使用过程中,遇到过这样一种情况,我需要新创建一个文件,然后直接用sed命令添加数据,我们来看一下直接这样来做会发生什么,我们由于要修改文件,所以就直接使用 -i 命令,我们这里使用向文件末尾追加的命令:
1 | !/bin/bash |
我们可以看到终端有以下输出:
1 | hk@ubuntu-22-04:~/1sharedfiles/6temp$ ./1.sh |
我们会发现文件依然是空的,没有任何数据,网上查了一下,发现是因为sed是基于行来处理的文件流编辑器,如果文件为空的话,它是处理不了的,那我们怎么处理呢?我们可以判断一下,当是空文件的时候使用echo来写,非空的时候用sed追加:
1 | !/bin/bash |
执行的结果如下:
1 | hk@ubuntu-22-04:~/1sharedfiles/6temp$ ./1.sh |
【说明】文章到这里就暂时结束了,没有遇到过相应的使用方式,笔记先到这里, sed 还有其他的用法,后续有用到再进行补充。