GNU sed 流编辑器
GNU sed 一个流编辑器(四)
标签:Linux sed 4.5版本 帮助文档 参考文档 全文翻译 随带20个示例 详细解析
版权声明:本文为博主原创译文,未经博主允许不得转载。https://blog.csdn.net/qq_39785418/article/details/89925036
5、 正则表达式:选择文本
5.1 sed正则表达式摘要
为了掌握如何使用sed,应该理解正则表达式(简称regexp)。正则表达式是从左到右匹配主题字符串的一种模式。其中的大多数字符都是普通的,它们在模式中仅代表自己,并匹配相应的字符本身。sed中的正则表达式指定在两个斜杠之间。下面的命令打印包含单词“hello”的所有行:
sed -n ’/hello/p’
上面的例子与这个grep命令等价:
grep ‘hello’
正则表达式的威力来自于可在模式中使用替代项和具有重复的能力。它们是通过在模式中使用特殊字符编码而得,这些特殊字符并不代表它们本身,而是以某种特殊的方式进行解释。
例如,正则表达式中的字符“^”(脱字符)与行首匹配。角色“.”(点)匹配任意单个字符。下面的sed命令匹配并打印以字母“b”开头、其次任何单个字符、再跟字母“d”的输入行:
$ printf "%s\n" abode bad bed bit bid byte body | sed -n ’/^b.d/p’
bad
bed
bid
body
下节讲解正则表达式中特殊字符的含义和用法。
5.2 基础正则表达式和扩展正则表达式
基础正则表达式(BRE)和扩展正则表达式(ERE)是模式中的两种语法变体。sed默认的正则表达式是BRE语法,与grep类似。使用POSIX指定的“-E”选项(或者“-r”、“–regexp-extended”)启用ERE语法。
在GNU sed中,基础正则表达式和扩展正则表达式之间的唯一区别是一小部分特殊字符的行为不同:?、+、(、)、{、}和 | 。
对于BRE语法,这些字符没有特殊意义,除非加前缀的反斜杠(“\”)才具有特殊意义;而对于ERE语法,这些字符刚好相反的:这些字符是特殊的,除非加前缀的反斜杠(“\”),才变成了字面意义上的字符。
期望模式 | BRE语法 | ERE语法 |
---|---|---|
字面上的 + (加号) | $ echo ‘a+b=c’ > foo $ sed -n ‘/a+b/p’ foo a+b=c | $ echo ‘a+b=c’ > foo $ sed -E -n ‘/a+b/p’ foo a+b=c |
一个或多个a字符后跟b(加号作为特殊元字符) | $ echo aab > foo $ sed -n ‘/a+b/p’ foo aab | $ echo aab > foo $ sed -E -n ‘/a+b/p’ foo aab |
5.3 基础正则表达式语法摘要
下面是sed中使用的正则表达式语法的简要描述。
char 单个普通字符匹配自己。
* 匹配字符“*”前面零个或多个正则表达式匹配实例的序列,这些正则表达式匹配实例必须是普通字符、前面加“\”的特殊字符、
“.”(句点)、分组的正则表达式(regexp,请参见下文)或方括号表达式。作为GNU的扩展,后缀正则表达式也可以跟随
在“*”后面;例如,“a**”等同于“a*”。POSIX 1003.1-2001宣称,当“*”出现在正则表达式或子表达式的开头时,它代表它
自己,但是许多非GNU实现版本不支持这个,可移植脚本应该在这些上下文中使用“\*”来代替它。
. 句点匹配任意单个字符,包括换行符。(译者:但是在多行模式下不匹配换行符!)
^ 匹配模式空间中最开始的空字符,例如,在脱字符“\^”后面出现的必须是在模式空间的开始处。
在大多数脚本中,模式空间初始化时读取第一输入行的内容(参见6.1节[sed如何工作]),所以,您可以这样理解,
把'^#include'想象成一行最开始位置出现字符串'#include'。但是,如果行前面有空格,匹配失败。只要不修改模式空间
的原始内容(例如使用s命令会修改),这种想法就有效。
“^”仅在正则表达式或子表达式(即“\(”或\“|”之后)的开头起特殊字符的作用。但是,可移植脚本应该避免在子表达式的开头
出现“^”,因为POSIX允许实现版本在该上下文中将“\^”视为普通字符的实现。
$ 它与“^”类似,但是它引用模式空间的结尾。“$”也在正则表达式或者子表达式(即在“\(”或“\|”之前)结尾起特殊字符作用,
且在子表达式结尾使用是没有移植性的。
[list]
[^list] [list]匹配列表list中的任意单个字符。例如,[aeiou]匹配所有元音。列表也可包含类似“char1-char2”,这会匹配“char1”
和“char2”及两者之间的所有任意单个字符,而[^list]则相反。参见5.5节[字符流和方括号表达式]。
\+ 匹配一个或多个的该“\+”前面实例的序列,等价于“\{1,\}”,这是GNU的扩展。
\? 匹配零个或者一个。等价于“\{0,1\}”,这是GNU的扩展。
\{i\} 完全匹配i个的序列(i是十进制整数;为了移植性,请将其保持在0和255之间(含0和255)。
\{i,j\} 匹配i至j个的序列,含i和j。
\{i,\} 匹配大于等于i个的序列。
\(regexp\)
将左右圆括号包围的“regexp”作为一个子表达式进行分组,可以用于:
1、应用后缀操作,例如表达式“\(abcd\)*”,这会搜索0次或多次重复的“abcd”,例如abcdabcd,而“abcd*”只会搜索“abc”
后面跟随的0个或多个字符“d”。注意,支持“\(abcd\)*”这样的格式要求实现POSIX 1003.1-2001规范,但是非GNU实现版本
不支持它,因此它不具有普遍移植性。
2、用于反向引用(参见下面“\digit”)。
Regexp1\|regexp2
要么匹配regexp1要么匹配regexp2。使用圆括号可以使用更复杂的可选正则表达式。匹配过程依次从左到右尝试每个选项,
并使用第一个成功的选项。它是GNU扩展。
Regexp1regexp2
匹配连接regexp1和regexp2后的表达式。连接比使用“\|”、“^”和“$”绑定得更紧密,但比其他正则表达式运算符绑定得更松。
\digit 引用匹配的第几(digit)个用“\(…\)”圆括号括起来的子表达式。这称为反向引用。从左到右,通过对正则表达式中左圆括号
“\(”出现的次数进行计数,来隐式地对子表达式进行编号。
\n 匹配换行符。
\char 匹配单个字符(char),该字符是“$”、“*”、“.”、“[”、“\”或“^”其中之一。请注意,只有这两个类似C语言的反斜杠序列
“\n和\\”,您可以假定它们具有可移植性;特别的是“\t”不可移植,在大多数sed实现版本中会匹配字符“t”,而不是制表符。
请注意,正则表达式匹配器是贪婪的,即从左到右尝试匹配,如果可以从同一个字符开始两个或多个匹配,则选择最长的。
实例:
abcdef 匹配 abcdef。
a*b 匹配0个或多个a跟随一个单字符b。例如,b或aaaaab。
a\?b 匹配b或ab。
a\+b\+ 匹配一个或多个a后跟随一个或多个b,ab是可能匹配中的最短的,其他示例是aaaab,abbbbb,aaaaaabbbbbbb。
.*
.\+ 这两个表达式都匹配字符串的所有字符;但是第一个匹配所有字符串,包括空字符串,而第二个只会匹配至少有一个字符的
字符串。
^main.*(.*)
匹配以字符串“main”开头,后面跟着一个左圆括号和一个右园括号。而n、(、)相互不必相邻。
^# 这将会匹配以#开头的字符串。
\\$ 这将会匹配以单反斜杠(\)结尾的字符串。该表达式包含两个反斜杠是为了转义。
\$ 相反,这将会匹配包含美元符号($)的字符串,因为它被转义了。
[a-zA-Z0-9]
在C语言环境中,它匹配任意单个ASCII字母或数字。
[^ tab]\+ 这会匹配由一个或者多个字符组成的字符串,但是该字符串中不能含有空格或制表符(译者:tab是指制表符,原文有多次这样
的表达,造成困惑!)。通常这意味着是一个单词。
^\(.*\)\n\1$
这会匹配由一个换行符分隔的两个完全一样的子字符串组成的字符串。
.\{9\}A$ 这会匹配行尾处是由9个字符紧跟一个A组成的字符串。
^.\{15\}A 这会匹配行首处是由15个字符紧跟一个A组成的字符串。
5.4 扩展正则表达式语法摘要
基础正则表达式和扩展正则表达式的唯一区别是这几个字符的行为不同:?、+、(、)、{、}和 |。如果您使用基础正则表达式,想让它们表现为特殊字符时,需要对它们转义;相反,如果您使用扩展正则表达式,想要以字面意义的字符本身去匹配它们时,必须对它们转义。“|”在这里是特殊的,因为“|”是GNU的扩展——标准的基础正则表达式不提供其功能。举例如下:
abc? 匹配ab或abc。而表达式“abc\?”会匹配字面字符串abc?。
c\+ 匹配c+。而表达式“c+”匹配一个或多个c。
a\{3,\} 匹配a{3,}。而表达式“a{3,}”匹配3个或更多的a。
\(abc\)\{2,3\}
匹配(abc){2,3}。而表达式“(abc){2,3}”会匹配abcabc或abcabcabc。
\(abc*\)\1 (译者:在扩展正则表达式中,该表达式会提示无效反向引用的错误。译者理解由于对圆括号进行了转义,就没有了分组,
而“\1”是反向引用,需要分组,测试如下:
$ echo 'abccabcc' | sed -r -n '/\(abc*\)\1/p'
sed: -e expression #1, char 12: Invalid back reference
$ echo 'abccabcc' | sed -n '/\(abc*\)\1/p'
abccabcc
$ echo 'abcabccc' | sed -r -n '/(abc*)\1/p'
abcabccc)
a\|b 匹配a|b。而表达式a|b会匹配a或者b。
5.5 字符类和方括号表达式
方括号表达式是使用“[”和“]”包围的列表。它可以匹配该列表中的任意单个字符;如果列表中的第一个字符是脱字符“^”,那么匹配的只能是不在该列表内的任意单个字符。例如,下面的命令用blue替换gray或grey:
sed ‘s/gr[ae]y/blue/’
方括号表达式可以用在5.3节[基础正则表达式]和5.4[扩展正则表达式],那就是说,不管有无使用“-E”或“-r”选项都可以使用。
在方括号表达式内,范围表达式由连字符(-)分隔的两个字符(含)组成。它匹配该两个字符及两者之间排序的任何单个字符。在默认的C语言环境中,排序顺序是本机字符顺序;例如,[a-d]等同于[abcd]。
最后,在括号表达式中已预定义了某些命名的字符类,如下所示。
这些命名的类必须用在方括号中(译者:相当于双方括号)。正确的用法是:
$ echo 1 | sed ‘s/[[:digit:]]/X/’
X
新版sed拒绝了不正确的用法。旧版会接受它,但将其视为单个方括号表达式。例如[dgit:]代表只有“d、g、i、t、:”这几个字符之一:
# 当前GNU sed版本——拒绝不正确的使用
$ echo 1 | sed ‘s/[:digit:]/X/’
sed字符类的语法是[[:digit:]],不是[:digit:]
# 旧版GNU sed版本
$ echo 1 | sed ‘s/[:digit:]/X/’
1
[:alnum:] (alphanumeric)字母数字类字符:[:alpha:]和[:digit:];在C语言环境中和ASCII字符编码中,与[0-9A-Za-z]
含义相同。
[:alpha:] (alphabetic)字母类字符:[:lower:]和[:upper:];在C语言环境中和ASCII字符编码中,与[A-Za-z]含义相同。
[:blank:] 空字符:空字符和tab制表符。
[:cntrl:] 控制字符。在ASCII中,这些字符的八进制编码是000至037,和177(DEL)。在其他字符集中,这些字符是等效字符
(如果有)。
[:digit:] 数字:0 1 2 3 4 5 6 7 8 9。
[:graph:] 图形化字符:[:alpha:]和[:punct:]。
[:lower:] 小写字母;在C语言环境中和ASCII字符编码中,就是26个小写字母。
[:print:] 可打印的字符:[:alnum:],[:punct:]和空白字符。
[:punct:] (punctuation)标点符号;在C语言环境中和ASCII字符编码中,就是:
! " # $ % & ’ ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ‘ { | } ~
[:space:] 空白字符:在C语言环境中,就是:tab制表符、换行符、垂直tab制表符、换页、回车和空格。包含[:blank:]。
[:upper:] 大写字符:在C语言环境中和ASCII字符编码中,就是26个大写字符。
[:xdigit:] (hexadecimal)16进制数字:0 1 2 3 4 5 6 7 8 9 A B C D E F
注意,这些类名中的方括号是符号类名的一部分,必须包含在分隔方括号表达式的方括号之外。
在方括号中许多元字符(译者:就是已经赋予了特殊含义的字符)失去了其特殊的含义:
] 如果它不是第一项,就起到结束左方括号表达式的作用。所以,如果您想让它成为列表项之一,必须要放在第一个。
- 如果它不是列表中的第一个或最后一个,或字符范围的结束点,都表示范围。(译者:也就是说,不放在最前或最后,在其他位置
都是表示范围的。)
^ 代表这些字符不在列表内。(译者:也就是取反)如果您想让它成为列表项之一,除了第一个位置外,其他位置都可以。
备忘录: 从基础正则表达式(BRE)节逐字复制并合并到此段落。
在列表中这些字符($、、[、\)不再具有特殊含义。例如,[*]匹配“\”或“”,因为这个“\”在这里不特殊。但是列表中像[.ch.]、[=a=]和[:space:]这样的字符串是特殊的,分别代表排序符号(collating symbols)、等价类(equivalent classes)和空白字符类,所以,当字符出现在列表中并跟随在“.”(句点)、“=”或“:”后面时是特殊的。此外,当不在POSIXLY_CORRECT模式下时,列表中可以识别像“\n”和“\t”这样的特殊转义。参见第5.8节[转义]。
(译者:以下内容从网上查找并翻译而来,排序符号和等价类主要用于多字节环境。
排序元素(collating element)可匹配任意的单个字符,或对应于某个单个单元的字符序列。它有两类名称:
一是连词,当下列连词用于排序名称时,都是有效的:
“ae”, “Ae”, “AE”, “ch”, “Ch”, “CH”, “ll”, “Ll”, “LL”, “ss”, “Ss”, “SS”, “nj”, “Nj”, “NJ”, “dz”, “Dz”, “DZ”, “lj”, “Lj”, “LJ”。
二是POSIX 符号名称:数量很多,请参考以下网页的介绍:https://help.alteryx.com/2018.4/boost/collating_names.html
这里摘录一些。
名称 | 字符 |
---|---|
NUL | \x00 |
SOH | \x01 |
STX | \x02 |
ETX | \x03 |
… | … |
下面几段翻译来源于: https://unix.stackexchange.com/questions/254811/what-does-ch-mean-in-a-regex。
排序元素通常在排序上下文中引用。在许多语言中,排序(类似于字典中的排序)不仅仅是按字符进行的。例如,在捷克,ch不像在英语中那样排序在cg和ci之间,而是作为一个整体进行排序。它是一个排序元素(我们不能在这里引用字符,字符是排序元素的子集),它排序在h和i之间。
为什么要在方括号表达式中引用排序元素?
在方括号内的表达式中,我们需要使用排序。例如,在[c-j]中,您希望字符位于c和j之间,您更希望在那里对元素进行排序。[h-i]在捷克地区语言中匹配ch:
$ echo cho | LC_ALL=cs_CZ.UTF-8 grep '^[h-i]o'
cho
因此,如果能够在方括号表达式中列出一组排序元素,那么也应该能够单独列出它们。[a-c[.ch.]]将匹配a和c之间或ch代表的排序符号的排序元素:
$ echo cho | LC_ALL=cs_CZ.UTF-8 grep '^[a-c[.ch.]]o'
cho
表明在a与c之间或在ch排序符号内。
现在,世界还不完美,也许永远不会完美。上面的例子是在GNU系统上运行的。排序元素的另一个例子是e,它在UTF-8中具有组合的重音($‘e\u0301’呈现为$’\u00e9’作为é)。
é和é 是相同的字符,除了一个是用一个字符表示,另一个用两个表示。
$ echo $'e\u301t\ue9' | grep '^[d-f]t'
在某些系统上可以正常工作,但不能在其他系统上正常工作(比如GNU系统)。目前还不清楚$’[[.\ue9.]]‘应该只匹配$’\ue9’或$’\ue9’和$‘e\u301’。
更不用说非字母脚本,或者具有不同区域性的、排序顺序的脚本,比如ffi(一个字符中的ffi),使用这样一个简单的API处理这些脚本会变得很棘手。
这是询问的人说的。这在使用非英语(非ascii)字符时非常有用。您提到的例子是连词,也就是说,一些语言的字母表中有些字母,可以用英语字母表中的两个字母来表示。
当您在regexp中使用[.ch.],您想说:“我期望得到连词ch的非英语输入序列。希望regexp与单个字符ch匹配。而编程语言表达式regex引擎或者键盘没有办法让我写出这个符号,所以只能输入了[.ch.],这并非意味着c后面跟着h,而是代表该单个字符的连词。”
[[.ch.]]表示的连词是一组字符集的一部分。在这种情况下,实际上只有一个字符,这是标准的regexp排序符号。
《上面网址的网页翻译到此为止》
排序符号匹配排序元素,也可以用作范围的端点,例如表达式“[[.ae.]-c]”,在假设“ae”为当前区域设置中的某个单个排序元素,可匹配字符序列“ae”,加上范围‘“ae”-c’中的任何单个字符。
等价类“[[=col=]]”,其中col是一个排序元素,匹配与该元素具有相同主权重的任何单个或多个字符的排序元素,也就是说,它们出现在当前语言环境中的排序序列的相同位置。由于排序元素的名称col可以是符号名称,所以这些字符或排序元素的主排序键与排序元素col的对照键相同。主排序键是忽略大小写、重音或特定区域设置的尾声的键;例如[[=a=]]匹配下列任何字符之一:a, À, Á, Â, Ã, Ä, Å, A, à, á, â, ã, ä and å。不幸的是,实现这一点依赖于平台的整理和本地化支持;不能依赖该特性实现跨所有平台、甚至不能跨越同一平台上的所有地区的可移植性。)
[./.] [.和.]分别代表排序符号的开始和结束。
[=/=] [=和=]分别代表等价类的开始和结束。
[:/:] [:和:]分别代表字符类的开始和结束。
[GNU sed 4.5 版参考文档全文翻译 各命令和随带20个示例详细解析(五)] (https://blog.csdn.net/qq_39785418/article/details/89968486)