【Shell】Shell命令-sed
摘要:
本文主要介绍Shell
中一个文本处理命令sed
。
自己本身也有在学Shell
编程,有些操作通过命令来进行却是比人为快得多,最近重写了自己博客下边的自动脚本文件,想到自己经常去重新初始化站点,重新安装主题,每一次也要敲不少的命令,也是比较繁琐,发现原来Shell
中有这样一个可以处理文本的命令,但是却要比平时所用的命令复杂得多。
1.sed
简介
sed
算是是一种在线编辑器,采用的是流编辑模式,它一次处理文件中一行的内容。
进行文本处理时,会把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space
),接着用sed
命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕,所以每处理一次就会有一次输出。接着处理下一行,这样不断重复,直到文件末尾。
【注意】sed
默认不会直接修改源文件数据,而是会将数据复制到缓冲区中,修改也仅限于缓冲区中的数据。
2.sed
命令
sed
命令,不管是否找到指定的模式,它的退出状态都是0
。只有当命令存在语法错误时,sed
的退出状态才不是0
。语法格式如下:
1 | sed [ option ] [ action ] file_name |
option
option | -n | 该选项会屏蔽启动输出,仅显示script处理后的结果 |
-i | 直接修改文件内容,而不是输出到终端。 | |
-e [script] | 以选项中指定的script来处理输入的文本文件,若之前已有命令,此时会将script脚本命令添加到已有的命令中。 | |
-f [script文件] | 以选项中指定的script文件来处理输入的文本文件,若之前已有命令,此时会将script文件中的脚本命令添加到已有的命令中。 |
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 的行。 |
3.sed
使用实例
只说参数并不能很好理解如何使用,这里将会有大量的实例来帮忙理解。先写一个测试用的文件,就以Hexo
的站点配置文件为例,删除一些东西,留下一些用于测试使用。文件内容如下:
1 | [1] title: Hexo |
【说明】我这里用的是Windows
下Git
的bash
,与linux
中应该是一样的。另外运行脚本中的$ ./mine.sh
表示在命令行运行该脚本文件,下边的为输出结果
3.1a和i
动作
- 在第
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 |
- 在第
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 |
3.2d
动作
- 通过行号指定删除行
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 |
- 通过区间删除行
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 |
- 通过数据搜索删除行
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.3c
动作
- 通过行号指定替换行
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 |
- 通过区间指定替换行
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 |
3.4-n选项与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 |
[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 |
会发现,含有theme
的行每行都被显示了两次,一次是处理的时候的输出,另一次是p
动作的输出。那么如何只输出我所需要的行呢?
-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 |
[7] theme: theme: landscape theme [8] theme: aaa [9] theme: landscape [10]theme: landscape |
3.5s动作
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 中用 \(\) 指定。 | |
\ | 转义(转义替换部分包含:&、\ 等)。 |
- 替换字符串的
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
选项则是替换所有的匹配值,这里不再说明。
- 显示替换行的
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
都会被替换。
- 匹配结果进行保存的
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 |
- 转义标识
\
主要是用于替换文件中的路径时用的较多。
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 |
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
表示退出。
3.6y动作
y
转换命令是唯一可以处理单个字符的 sed
脚本命令。
1 | [address]y/inchars/outchars/ |
转换命令会对 inchars
和 outchars
值进行一对一的映射,即 inchars
中的第一个字符会被转换为 outchars
中的第一个字符,第二个字符会被转换成 outchars
中的第二个字符…,这个映射过程会一直持续到处理完指定字符。如果 inchars
和 outchars
的长度不同,则 sed
会产生一条错误消息。
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 |
3.7r
动作
创建一个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 |
3.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
替换。
3.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 |
【说明】可以看到,命令直接替换了所有的字符串。
3.10-f 选项
sed
脚本
sed
脚本就是写在文件中的一列sed
命令。脚本中,命令的末尾不能有任何多余的空格或文本。如果在一行中有多个命令,要用分号分隔。使用sed
脚本时,不再用引号来确保sed
命令不被shell
解释。
执行脚本时,sed
先将输入文件中第一行复制到模式缓冲区,然后对其执行脚本中所有的命令。每一行处理完毕后,sed
再复制文件中下一行到模式缓冲区,对其执行脚本中所有命令。
【说明】文章到这里就暂时结束了,没有遇到过相应的使用方式,笔记先到这里,sed
还有其他的用法,后续有用到再进行补充。