sed是一个“作交互式的”面向字符流的编辑器。输入一般来自文件,但是也可以直接来自键盘。输出在默认情况下输出到终端屏幕上,但是也可以输出到文件中,sed通过解释脚本来工作,该脚本指定了将要执行的动作。
sed还可以作为编辑过滤器使用。换句话说,你可以处理输入文件并将输出结果发送到另外一个程序。
下面是 sed 能够完成的一些功能:
awk 程序的典型示例是将数据转换成格式化的报表。当数据拥有某种结构时就能最好地体现awk的好处。
【awk 命令详解】https://blog.csdn.net/qq_42226855/article/details/135089290
下面是awk能够完成的一些功能:
可以将 awk 的起源追溯到 sed 和 grep,并且经由这两个程序追溯到 ed 命令,ed是最初的unix行编辑器。(ed 命令的用法可自行了解,不是本文重点)
sed 与 ed 命令用法比较像。例如,ed 中的替换命令s:
[address]s/pattern/replacement/flag
pattern是一个正则表达式,可以用replacement替代当前行中与这个正则表达式匹配的字符串。
s/regular/complex/
由于没有指定 flag,所以它只影响当前行上的第一次出现(默认)。如果在当前行上没有找到“regular”则出现一个错误。为了寻找同一行上的多次出现,必须指定 g 作为标志:
s/regular/complex/g
这个命令仅仅改变了当前行上的所有的出现。可以用正则表达式指定地址,从而使该命令不只是对当前行操作。下面的替换命令指定了一个地址:
/regular/s/regular/complex/g
# 等价写法,简写(地址与匹配模式相同时,可以省略)
/regular/s//complex/g
这个命令影响与这个地址匹配的第一行。记住,第一个“regular”是一个地址,第二个是匹配替换命令的模式。要将它应用于所有的行,必须使用全局命令,既在地址前放置g:
g/regular/s/regular/complex/g
现在,这个替换应用于所有的地方,即所有行上的所有出现。
类似的unix实用工具 grep 来源于 ed 中的下面的全局命令,它表示“全局正则表达式打印”:
g/re/p
sed是作为特殊目的的编辑器而创建的,用于专门执行脚本;与ed 不同,它不能交互使用。Sed与ed的主要区别在于它是面向字符流的。默认情况下,到 sed 的所有输入都会经过相应的处理,并转为标准输出。输入文件本身不发生改变。如果确实想改变输入文件,一般使用shell机制进行输入重定向,当你对所做的编辑工作满意时,用修改后的版本代替最初的文件。
ed 不是面向字符流的,并且文件本身会发生改变。Ed 脚本必须包含保存文件并退出编辑器的命令。它不产生到达屏幕的输出,但由特殊命令生成的东西除外(ed 命令的用法可自行了解,不是本文重点)。
sed的字符流定位对如何应用寻址有重要影响。在ed 中没有指定地址的命令,只影响当前行。sed遍历文件,每次一行,这样每一行都成为当前行,而且每行都应用这个命令,结果是sed对文件中的每一行应用了没有地址的命令。
注意:理解ed中的当前行寻址与sed中全局行寻址之间的区别是很重要的。在ed 中,使用寻址扩大受命令影响的行数;在sed 中,使用寻址限制受命令影响的行数。
awk 是作为可编程的编辑器而开发的,同sed一样,它也是面向字符流的,并且解释编辑命令的脚本。Awk 与sed不同的地址是它废弃了行编辑器的命令集。它提供了仿效C语言的程序设计语言,例如,print 语句取代了 p 命令;但是延续了寻址的概念,例如:
/regular/{print }
在脚本中使用程序设计语言的优点是,它提供了更多的方式来控制可编程的编辑器所做的事情。awk 提供了表达式、条件语句、循环和其他程序设计结构。awk 最独特的特征之一是它分析或拆分每个输入行,并生成可用于脚本处理的独立的单词。
sed 和 awk 一个共同的选项是 -f 选项,这个选项允许你指定脚本文件的名字。可以将它放置在文件中比较方便。因此,可以按如下方式调用:
sed -f scriptfile inputfile
awk -f scriptfile inputfile
下图展示了sed和awk 的基本操作。每个程序每次从输入文件中读取一个输入行,生成该输入行的备份,并且对该备份执行脚本中指定的指令。因此,对输入行所做的改动不会影响真正的输入文件。
当读取输入的每行时,程序读取脚本中的第一个指令并检测当前行的模式。如果没有匹配,这个过程被忽略并读取下一个指令。如果有一个不匹配,那么执行过程中指定的一个或多个动作。读取所有的指令,而不只是读取与输入行匹配的第一条指令。
当所有可用指令被解释并应用于单个行后,sed输出该行并循环处理每个输入行(-n 选项 或 #! 脚本开头,可以阻止 sed 输出;默认情况下,无论是否处理该文本行,最终都会输出所有的行)。另一方面,awk 不自动输出行,脚本中的指令控制awk最终所做的事情。
在sed和awk中过程的内容有很大不同。在sed 中,过程由类似于行编辑器中使用的那些编辑命令组成。大部分命令由单个字母组成。在awk中,过程由程序设计语句和函数组成。过程必须用大括号括起。
调用 sed 有两种方法:在命令行上指定编辑命令,或者将它们放到一个文件中并提供这个文件的名字。
可以在命令行上指定简单的编辑命令。
sed [-e] 'instruction' file
只有在命令行上给出多个指令时才需要用 -e 选项。它告诉 sed 将下一个参数解释为指令。当只有一个指令时, sed 可以省略 -e 选项。
并不是在任何情况下都需要将指令用单引号包围起来,但是你应该养成这个习惯。使用单引号可以阻止 shell 解释编辑指令中的特殊字符或空格(shell 使用空格决定提交给程序独立参数,特殊的shell 字符在调用之前被展开)
有 3 种方式可以指定命令行上的多重指令:
sed 's/www/www.ufo.com/;s/www1/www1.ufo.com/' list
sed 's/www/www.ufo.com/' -e 's/www1/www1.ufo.com/' list
sed '
> s/www/www.ufo.com/
> s/www1/www1.ufo.com/' list
sed 通常会显示任何它不能执行的行,但是它不会告诉你使用这个命令所发生的错误时什么;awk 的错误提示比 sed 明确一些。
在命令行上输入较长的编辑脚本是不实际的,这就是通常最好创建包含编辑指令的脚本文件的原因。
sed -f scriptfile file
cat scriptfile
s/www/www.ufo.com/
s/www1/www1.ufo.com/
s/www2/www2.ufo.com/
如果脚本能再次使用,那么应该重新命令这个脚本并保存它。
显示在屏幕上的结果是临时的,输入文件中没有发生改变。只有将 sed 的输出重定向到另一个程序中,才能够捕获文件中的输出。
sed -f scriptfile file > newfile
不要将输出重定向到正在编辑的文件中,否则就会使它变成乱码。可以使用mv 命令作为单独的不足来处理保存。
sed 的默认操作是输出每个输入行。-n 选项可以阻止自动输出。当指定该选项时,每个要生成输出的指令都必须包含打印命令p。这里只会显示受命令影响的行。
sed -n -e 's/www/www.ufo.com/p' file
每次从一个或多个文件中读入一行或从标准输入中读入一行。指令必须包含在单引号中,从而与shell 区别开。可以用与 sed 相同的方式输入多重命令行:用分号分隔命令、多行输入功能。
awk -f script files
在通常情况下,awk 将每个输入行解释为一条记录而将上一行的每个单词(由空格或制表符分隔)解释为每一个字段(可以改变这些默认设置)。一个或多个连续的空格或制表符看作一个定界符。awk 允许在模式或过程中引用这些字段。$0 代表整个字段,$1 … $n 代表第1个到第n个字段。
awk '{print $1}' list
awk '/www/' list
awk '/www/{print $1}' list
使用 -F 选项将字段分隔符号改变为其他符号
awk -F, '/www/{print $1}' list
awk -F, '/www/{print $1;print$2;print$3}' list
可以通过管道符传递。
awk -F, '/www/{print $1}' list | sort | uniq -c
awk -F, '/www/{print $1}' list | sed '/ww/ww.ufo.com/' |sort | uniq -c
某些其他程序为使用正则表达式提供了不区分大小写的选项,但是 sed 和 awk区分大小写。
扩展元字符(egrep 和 awk):
元字符反斜杠(\)将元字符转换成普通字符(以及将普通字符转换成元字符)。它强制将任意元字符解释为普通文字,以便匹配该字符本身。
\.
\\
另外,sed 使用反斜杠将一组普通字符被解释为元字符。
\( \)
\{ \}
\n # n 代表 1-9 的数字
通配符不匹配换行符。
[root@ufo666 ~]# echo -e "This is First Line.\nThis is Second Line\n" | sed '/T/s/Line./666/'
This is First 666
This is Second Line
字符类是对通配符概念的改进。我们可以列出要匹配的字符,使用方括号元字符 “[ ]” 将字符列表括起来,其中每个字符占据一个位置。字符类在处理大写和小写字母时非常有用。例如,如果“what”可能以首字母大写或小写的形式出现,则可以指定:
[Ww]hat
注意,必须用引号引住其中的模式,以便把它传递给 sed、awk,而不只是由shell解释。
.[?!,;;.].
注意,在方括号中、标准的元字符会失去它们的含义。因此,方括号中的点表示一个句点。
字符类当中的特殊字符:
反斜杠只在awk中是特殊的,如下可以使用字符类匹配一个a、一个]、一个1。
[a\]1]
[a-zA-Z0-9]
为了匹配算术操作符,我们在下面的示例中将连字符 “-” 放在第一位:
[-+*/]
通常,字符类包括在哪个位置想要匹配的所有的字符。在类中作为第一个字符的脱字符(^)将类中的所有字符由排除在被匹配之外。相反,除换行符以外的没有列在方括号中的任意字符都将匹配。下面的模式将匹配任意非数字字符:
[^0-9]
[root@ufo666 ~]# echo -e "This is First Line.\nThis is Second Line\n" | sed '/T/s/Line[^0-9]/666/'
This is First 666
This is Second Line
注意,awk 时可以匹配换行符的;^ 字符在shell中时特殊字符,应用时必须用单引号括住。
POSIX标准对正则表达式字符和操作符的含义进行了形式化。这种标准定义了两类正则表达式:基本的正则表达式(BRE) , grep和sed使用这种正则表达式;扩展的正则表达式,egrep和awk使用这种正则表达式。
星号(*)元字符表示它前面的正则表达式可以出现零次或多次。
问号(?)匹配零次或一次出现。因为元字符问号在sed中不可用,我们必须求助于字符类,这些在后面将会看到。
有两个元字符用于指定字符串出现在行首或行末的上下文。脱字符(^)元字符是指示开始的单字符正则表达式。美元符号($)元字符是指示行结尾的单字符的正则表达式。这些通常称为“定位符”,因为它们将匹配限定在特定位置。例如,可以使用以下表达式打印以666开始的行:
^666
注意,在sed(和grep)中,只有当 在sed(和grep)中,只有当 “^” 和 “$” 分别出现在正则表达式的开始或结尾时才是特殊的。
注意,在awk中则不同,“^” 和 “$” 总是特殊的,即使它们可能使编写的正则表达式不匹配任何东西。可以说,在awk中,当想要匹配字面 “^” 或 “$” 时,不管它处于正则表达式的什么位置都应该用反斜杠对其进行转义。
模式匹配的程序(例如grep)不能匹配跨两行的字符串。稍后将在介绍sed时,介绍如何匹配多行模式,支持跨行匹配。
在 grep 和 sed 中使用 \{ 和 \} 。POSIX egrep 和 POSIX awk 使用 { 和 } 。在任何情况下,大括号包围一个或两个参数。n和m 是 0-255 之间的整数。
\{n,m\}
竖线(|)元字符是元字符扩展集的一部分,用于指定正则表达式的联合。如果某行匹配其中的一个正则表达式,那么它就匹配该模式。例如,正则表达式:
UNIX|LINUX
圆括号()用于对正则表达式进行分组并设置优先级。它们是元字符扩展集的一部分。假设在文本文件中将公司的名称为“BigOne”或“BigOne Computer”,使用表达式:
BigOne (Computer)?
可以使用竖线和圆括号来对选择性操作进行分组。在下面的示列中,我们使用它来指定与单词“company”的单数或复数匹配。
compan(y|ies)
注意,在大多数sed和grep的版本中不能对加圆括号的一组字符应用数量词,但是在egrep和awk的所有版本中都是可以的。
注意,因为 | 和 ()是元字符的扩展集的一部分,所以如果使用的是sed,则必须编写不同的表达式来处理每一种情况。
正则表达式尝试匹配最长的字符串,并且这可能会引起意想不到的问题。例如,查看以下正则表达式(尽可能的写详细表达式):
# 6667、666777、66666688888888
666.*
sed 工作的3个基本的原理:
sed维护一种模式空间,即一个工作区或临时缓冲区,当应用编辑命令时将在那里存储单个输入行。如下图进行模式空间转换的一个两行的脚本。它将 “The Unix System” 改变为 “The UNIX Operating System”。
注意:一次一行的设计的一个优点是sed在读取非常庞大的文件时不会出现问题。一般的屏幕编辑程序必须将整个文件(或者它的一些庞大的部分)读入内存,这将会产生内存溢出或者在处理庞大的文件时速度非常慢。
初始时,模式空间包含有单个输入行的备份。如上图 “The Unix System”行。脚本中正常的流程是在这一行上执行每个命令直到脚本达到末尾。脚本中的第一个命令应用于这一行,将 “Unix” 换成 “UNIX” 。然后应用第二个命令,将“UNIX” 换成“UNIX Operating System”。注意第二个替换命令的模式不匹配最初的输入行,它匹配模式空间中发生了变化的当前行。 当应用了所有的指令后,当前行被输出并且输入的下一行被读入模式空间。然后脚本中的所有的命令应用与新读入的行。模式空间的内容是动态的,而且并不总是匹配最初的输入行。
要注意的是sed将命令应用于每个输入行。与ed、ex或vi不同,sed是隐式全局的。下面的替换命令将 每个匹配到的 “CA” 行的 每个 “CA”字符串都换成“California”。
s/CA/California/g
sed命令可以指定零个、一个或两个地址。每个地址都是一个描述模式、行号或者行寻址符号的正则表达式。
为了解释寻址是如何工作的,让我们先来看看使用删除命令 d 的示例。一个只由 d 命令组成并且没有地址的脚本不会产生输出,因为它删除了所有的行,改变了模式空间的内容:
d
当行号作为一个地址提供时,命令只影响那一行。例如,下面的示列只删除第一行:
1d
行号指由sed维护的内部行数。该计数器不会因为多个输入文件而重置。因此,不管指定多少个输入文件,在输入流中也只有一行,sed 会累计并维护行数。
同样,输入流也只有一个最后的行。可以使用寻址符号$指定。下面的示列删除输入的最后一行:
$d
$符号不应该和正则表达式中使用的$相混淆,在这里表示行的结束。
当正则表达式作为地址提供时,这个命令只影响与这个模式匹配的行。正则表达式必须封闭在斜杠(/)中。下面的删除命令:
/^$/d
只删除空行。所有其他行都不会改变。
如果提供两个地址,那么就指定了命令执行的行范围。下例展示了如何删除由一对寻址地址包围的所有行:
/^1.TS/,/^100.TS/d
它删除了从第一种模式匹配的行开始,到由第二种模式匹配的行(包括此行在内)为止的所有行。这个范围之外的行不受影响。
下面的命令删除了文件中从行50到最后一行的所有行:
50, $d
可以混合使用行地址和模式地址:
1,/^$/d
这个示例,删除了从第一行直到第一个空行的所有的行。
注意:你可以把第一个地址看做是启用动作,并把第二个地址看做是禁用动作。Sed没有把法先行决定第二个地址是否会匹配。一旦匹配了第一个地址,这个动作就将应用于这些行。于是命令应用于“所有”随后的行直到第二个地址被匹配。在上例中,如果文件不包含空行,那么将删除所有的行。
跟在地址后面的感叹号会反转匹配的意义。例如,下面的脚本将删除了在寻址范围以外的所有行:
/^\.TS/,/^\.TE/!d
sed使用大括号 { } 将一个地址嵌套在另一个地址中,或者在相同的地址上应用多个命令。如果想指定行的范围,然后在这个范围内指定另一个地址,则可以嵌套地址。例如,使用下面的命令:
/^\.TS/,/\.TE/{
/^$/d
}
左大括号必须在行末,而且右大括号本身必须单独占一行。要确保在大括号之后没有空格。
测试并保存输出
在前面关于模式空间的讨论中,可以看到sed :
1.生成输入行的备份。
2.修改模式空间中的备份。
3.将备份输出到标准输出。
如果想要保存这些输出,就必须将它们收集到一个新文件中。
sed -f sedscr testfile > newfile
其中,重定向符号 “>” 将来自sed的输出直接送往文件newfile中。不要将来自命令的输出重定向到输入文件,否则会改写输入文件,甚至可能在sed处理这个文件之前发生,并破坏你的数据。
将输出重定向到文件的一个重要的理由是要检验结果。可以使用diff程序指出两个文件之间的区别。
diff testfile newfile
在shell脚本中可以使用位置符号来指定命令行上的每个参数:第一个参数是$1,第二个参数是$2,以此类推。
#!/bin/sh
getmac -- print mm macro definition for $1
sed -n "/^\.de$1/,/^1.\.$/p" /usr/lib/macros/mmt
mac="$1"
file="$2"
sed -n "/^\.de*$mac/,/^\.\.$/p" $file
sed '/---/!s/--/\\(em/g' $file | ...
注意,括住sed脚本的 双引号 是必须的。如果使用单引号,则shell不对“$1”进行解释。
行地址对于任何命令都是可选的。它可以使一个模式,被描述为由斜杠、行号或行寻址符号括住的正则表达式。大多数sed命令能接受由逗号分隔的两个地址,这两个地址,这两个地址用来标识行的范围。这些命令的语法格式为:
[address]command
有一些命令只接受单个行地址。它们不能应用于某个范围的行。它们的语法格式为:
[line-address]command
记住命令还可以用大括号进行分组以使其作用于同一个地址:
address {
command1
command2
command3
}
第一个命令可以和左大括号放置在同一行,但是右大括号必须自己单独处于一行。每个命令都可以有自己的地址并允许有多层分组。而且,就像命令在大括号内的缩进方式一样,允许在行的开始处插入空格和制表符。
当sed不理解一个命令时,它打印出消息“Command garbled(命令不清)”。在命令后添加空格会产生一个小的语法错误,这个不允许的,命令的结束必须在行的结尾处。
如果命令之间用一个分号分隔,那么可以将多个sed命令放在同一行。下面的示例在语句构成上是正确的:
n;d
然而,在n命令后面放置一个空格会导致语法错误,而在d命令前面放置一个空格是可以的。
不提倡在同一行放置多个命令,因为即使将这些命令写在各自的行上,sed 脚本也是很难阅读的(注意,更改、插入和追加命令必须在多行上指定,不能在同一行上指定)。
可以使用注释描述脚本的作用。注释行的第一个字必须是“#”号。注释行的语法如下:
#[n]
下列显示了一个脚本的第一行:
# wstar. sed: convert WordStar files
我们已经讨论了替换命令的许多用法。下面是它的详细的用法:
[addres]s/pattern/replacement/flags
这里修饰替换的标志flags是:
替换命令应用于与address匹配的行。如果没有指定地址,那么就应用于Pattern匹配的所有行。如果正则表达式作为地址来提供,并且没有指定模式,那么替换命令匹配由地址匹配的内容。
s/www/fff/g
# 两种等价写法
/www/s/www/fff/g
/www/s//fff/g
和地址不同的是,地址需要一个作为定界符的斜杠(/) ,而正则表达式可以用任意字符来分隔,只有换行符除外。因此,如果模式包含斜杠,那么可以选择另一个字符作为定界符,例如感叹号。
s!/usr/mail!/usr2/mail!
/www/s!/usr/mail!/usr2/mail!
/www/s!\!/usr/mail!\!/usr2/mail!
注意,定界符出现了3次而且在replacement之后是必需的。不管使用哪种定界符,如果它出现在正则表达式中,或者在替换文本中,那么就用反斜杠来转.
义它。
因为在内部存储时换行符只是一个字符,所以正则表达式可以使用“\n”来匹配嵌入的换行符(单行模式匹配不到,多行模式可以,多行模式后边会介绍)。
s/ww \n w/fff/g
Replacement是一个字符串,用来替换与正则表达式匹配的内容在replacement部分,只有下列字符有特殊含义:
因此,除了正则表达式中的元字符以外,sed 的替换部分也有元字符。
flag可以组合使用,只要有意义。例如,gp表示对行进行全局替换并打印这一
行。迄今为止,全局标志是最常用的。没有它,替换只能在行的第一次出现的位置执行。
打印标志(p)和写标志(W file) 与 打印命令(-n)和写命令(-i) 的功能相同,但有一个重要的区别。打印标志(p)和写标志(W file) 这些操作是随替换的成功而发生的。换句话说,如果进行了替换,那么这个行被打印或写到文件中。因为默认的动作是处理所有的行,不管是否执行了任何动作,当取消默认的输出时(加-n选项),通常使用打印标志和写标志配合使用。另外,如果脚本包含匹配同一行的多个替换命令,那么那一行的多个备份就会被打印或写到文件中。
数字表示很少使用,在这种情况下,正则表达式在一行上重复匹配,而只需要对其中某个位置的匹配进行替换。
s/666/>/2
注意,如果没有数字标识,则替换命令只替换第一个匹配的字符串 ,因此“1“可以被看作是默认的数字标志。
替换元字符是反斜杠(\)、“与” 符号(&)和\n。反斜杠一般用于转义其他
的元字符,但是他在替换字符串中也用于包含换行符。我们可以对前面的示例做一些改动,用换行符取代每行上的第二个制表符。
s/●/\
/2
注意,在反斜杠后面不允许有空格。
当正则表达式匹配单词的变化时,“ 与”符号特别有用。它允许指定一个可变
的替换字符串,”与“ 符号用于在替换字符串中引用整个匹配内容。
s/6666/\\1-&/g
现在,我们来看一种元字符,它用于选择被匹配的字符串的任意独立部分,并且在替换字符串中回调它。在sed中用转义的圆括号括住正则表达式的任意部分并且保存它以备回调。一行最多允许“保存”9次。“\n” 用于回调被保存的
匹配部分,n是从1到9的数字,用于特殊“保存的”备用字符串。
s/\(6666\)\(777\)/\1\\-\2\\/
前面已经展示了删除命令(d)的示例。它采用一个地址,如果行匹配这个地址就删除模式空间的内容。
删除命令还是一个可以改变脚本中的控制流的命令。这是因为一旦执行这个命令,那么在“空的”模式空间中就不会再有命令执行。删除命令会导致读取新输入行,而编辑脚本则从头开始新的一轮(这一行为和next命令的行为相同,后面将介绍next命令)。
重要的是:如果某行匹配这个地址,那么就删除整个行,而不只是删除行中匹配的部分(要删除行的一部分,可以使用替换命令并指定一个空的替换)。
删除空行的命令:
/^$/d
注意,不允许在被删除的行上进一步操作。模式空间的内容被清除后,会导致读取新输入行,sed 脚本从头开始。
删除命令可以用于删除一个范围内的行。在上一章中,有一个通过删除\.TS和\.TE之间的行来删除文件中的所有表格的示例。还有一个删除命令(D)用于删除多行模式空间的一部分。这个高级命令将在下一章介绍。.
/\\.TS/,\\.TE/d
追加(a)、插入(i)和更改?命令提供了通常在交互式编辑器(例如vi)中所选的编辑功能。这些命令的语法在sed中不常用,因为它们必须在多行上来指定(标准写法)。语法如下:
追加[line-address]a\
text
插入[line-address]i\
text
更改[address]c\
text
插入命令将所提供的文本放置在模式空间的当前行之前。追加命令将文本放置在当前行之后。更改命令用所提供的文本取代模式空间的内容。
标准写法是多行书写编辑命令,下边是单行书写编辑命令,不易阅读:
[root@ufo666 ~]# echo -e "AAA \nBBB \nCCC \nDDD\n" | sed '/BBB/,/CCC/c\UFO'
AAA
UFO
DDD
[root@ufo666 ~]# echo -e "AAA \nBBB \nCCC \nDDD\n" | sed '/BBB/,/CCC/c\UFO\nUFO\nUFO'
AAA
UFO
UFO
UFO
DDD
这些命令中的每一个都要求后面跟一个反斜杠用于转义第一个行尾。text 必须从下一行开始。要输入多行文本,每个连续的行都必须用反斜杠结束,最后一行例外。如果文本包含一个字面的反斜杠,要再添加一个反斜杠来转义它。
多行文本输入,标准写法:
[root@ufo666 ~]# echo -e "AAA \nBBB \nCCC \nDDD\n" | sed '/BBB/,/CCC/c\
> ufo\\-01\
> ufo\\-02\
> ufo\\-03'
AAA
ufo\-01
ufo\-02
ufo\-03
DDD
追加命令和插入命令只应用于单个行地址,而不是一个范围内的行。然而,更改命令可以处理一个范围内的行。在这种情况下,它用一个文本备份取代所有被寻址的行。换句话说,它删除这个范围中的所有行,但是提供的文本只被输出一次。
注意,当更改命令作为一组命令之一被封闭在大括号中并作用于一个范围内的行时,它将具有相反的功能。
例如,下面的脚本:
[root@ufo666 ~]# echo -e "AAA \nBBB \nCCC \nDDD\n" | sed '/BBB/,/DDD/{
> c\
> ufo\\-01\
> ufo\\-02
> }'
AAA
ufo\-01
ufo\-02
ufo\-01
ufo\-02
ufo\-01
ufo\-02
将对这个范围内的每行输出 ufo\-01、ufo\-02 。所以,当前面的示例输出文本一次时,这个示例会输出3次,如果在这个范围内有3行的话。
更改命令清除模式空间,它在模式空间中与删除命令有同样的效果。脚本中在更改命令之后的其他命令没有被提供。
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\nDDD' | sed '/BBB/c\UFO' | grep 'UFO'
UFO
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\nDDD' | sed '/BBB/c\UFO' | grep 'BBB'
[root@ufo666 ~]#
插入命令和追加命令不影响模式空间的内容。提供的文本将不匹配脚本中后续命令中的任何地址,那些命令也不影响该文本。不管什么更改改变了模式空间,所提供的文本仍然会正确地输出。当默认的输出受到抑制时也是这样,所提供的文本将被输出,即使模式空间不是那样的。而且,所提供的文本不影响 sed 的内部行计数器。
# 插入
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\nDDD' | sed '/BBB/i\UFO' | grep 'UFO'
UFO
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\nDDD' | sed '/BBB/i\UFO' | grep 'BBB'
BBB
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\nDDD' | sed '3i\UFO'
AAA
BBB
UFO
CCC
DDD
# 追加
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\nDDD' | sed '/BBB/a\UFO' | grep 'UFO'
UFO
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\nDDD' | sed '/BBB/a\UFO' | grep 'BBB'
BBB
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\nDDD' | sed '$a\UFO'
AAA
BBB
CCC
DDD
UFO
更改命令用所提供的文本取代模式空间的内容。实际上,它删除行并且在该位置应用所提供的文本。当想要匹配行并且整个取代它时可以使用这个命令。
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\nDDD' | sed '2,3c\'
AAA
DDD
列表命令(I)用于显示模式空间的内容,将非打印的字符显示为两个数字的 ASCII 代码。可以使用该命令来检测输 入中的 “不可见” 字符。
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\nDDD' | sed l
AAA$
AAA
BBB$
BBB
CCC$
CCC
DDD$
DDD
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\nDDD' | sed -n l
AAA$
BBB$
CCC$
DDD$
因为列表命令产生立即的输出,所以我们抑制了默认的输出,否则将得到行的 重复复制。
在 sed 中不能用于 ASCII 值匹配字符,也不能匹配八进制数值(然而 awk 中可以)。但是可以用键盘组合键来产生它。 使用CTRL-V引用该字符 。例如,可以输入(CTRL-V) + M、(CTRL-V) + N 等:
[root@ufo666 ~]# echo -e 'AAA\nBBB\n^M^M^M\n^N^N^N' | sed l
AAA$
AAA
BBB$
BBB
\r\r\r$
\016\016\016$
去除不可打印的字符,有时去除这些打印的 “特殊效果” 是有必要的,特殊符号需要键盘输入**(CTRL-V)+ H**,复制的文本不行:
[root@ufo666 ~]# echo -e 'AAA\n^H' | sed -n l
AAA$
\b$
[root@ufo666 ~]# echo -e "AAA\n^H" | sed -n 's/^H/666666/p'
666666
sed 不可以匹配换行符号 “\n”,也不可以替换(多行模式待定? awk 待定 ?),正则表达式不能匹配换行符号:
[root@ufo666 ~]# echo -e 'AAA\nBBB' | sed -n -e "/\n/p"
[root@ufo666 ~]#
转换命令(y)是特有的。这个命令按位置将字符串 abc 中的每个字符,都转换成字符串 xyz 中的等价字符 。它的语法如下:
[address]y/abc/xyz/
说明:这个命令在 UNIX tr 命令之后被淡化,被用于转换字符,例如转换大小写
y/abcdefghijkImnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
打印命令§输出模式空间的内容。它既不清除模式空间也不改变脚本中的控制流。然而,它频繁地用在改变流控制的命令(d,N,b)之前。除非抑制(-n)默认的输出,否则打印命令将输出行的重复复制。当抑制默认的输出或者当通过程序的流控制来避免到达脚本的底部时,可能会使用它。
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\nDDD' | sed 's/AAA/UFO/g'
UFO
BBB
CCC
DDD
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\nDDD' | sed 's/AAA/UFO/gp'
UFO
UFO
BBB
CCC
DDD
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\nDDD' | sed -n 's/AAA/UFO/gp'
UFO
注意,打印标志被提供给替换命令时,它是以成功的替换为条件的。
跟在地址后面的等号(=)打印被匹配的行的行号。除非抑制行的自动输出,行号和行本身将被打印。它的语法如下:
[line-address]=
这个命令不能对一个范围内的行进行操作。
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\nDDD' | sed '/AAA/='
1
AAA
BBB
CCC
DDD
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\nDDD' | sed -n '/AAA/='
1
下一步(next)命令(n)输出模式空间的内容,然后读取输入的下一行,而不用返回到脚本的顶端。它的语法如下:
[address]n
next 命令改变了正常的流控制,直到到达脚本的底部才会输出模式空间的内容,它总是在读入新行之后从脚本的顶端开始。实际上,next 命令导致输入的下一行取代模式空间中的当前行。脚本中的后续命令应用于替换后的行,而不是当前行。如果没有抑制默认输出,那么在替换发生之前会打印当前行。
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC' | sed '/AAA/{n;p}'
AAA
BBB
BBB
CCC
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC' | sed -n '/AAA/{n;p}'
BBB
# 抑制输出时,并不会打印输出。
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC' | sed -n '/AAA/{n;p;s/BBB/UFO666666/p}'
BBB
UFO666666
# 第二次匹配 /AAA/ 时,已经改变了模式空间的内容。
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC' | sed '/AAA/{n;p;s/BBB/UFO666666/p}'
AAA
BBB
UFO666666
UFO666666
CCC
注意,sed 中的 n 操作会读取下一行并将其存储到模式空间中,但默认情况下并不会打印这一行。当你使用了 -n 参数时,由于已经关闭了默认的打印行为,所以即使执行了 n 命令也不会打印出任何内容。
读?和写(w) 命令用于直接处理文件。这两个命令都只有一个参数,即文
件名。语法如下:
[line- address]r file
[address]w file
读命令将由file指定的文件确定的行之后的内容读入模式空间。它不能对一个范围内的行进行操作。写命令将模式空间的内容写到file中。
在命令和文件名之前必须有一个空格(空格后到换行符前的每个字符都被当做文件名。因此,前导的和嵌入的空格也是文件名的一部分)。如果文件不存在,读命令也不会报错。如果写命令中指定的文件不存在,将创建一个文件;如果文件已经存在,那么写命令将在每次调用脚本时改写它。如果一个脚本中有多个指令写到同一个文件中,那么每个写命令都将内容追加到这个文件中。而且,每个脚本最多只能打开10个文件。
/^UFO.*$/r file
注意,使用-n选项或#n脚本语法可以取消自动输出,阻止模式空间的初始行被输出,但是读命令的结果仍然转到标准输出,不受抑制选项限制。
写命令在被调用时就写出模式空间的内容,而不是等到到达脚本的结尾时才进行写操作,不受抑制输出影响。
/^UFO.*$/w file
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC' | sed '/AAA/w output.txt'
AAA
BBB
CCC
[root@ufo666 ~]# cat output.txt
AAA
退出命令(q)会使sed停止读取新的输入行(并停止将它们发送到输出)。它的语法为:
[line-address]q
它只适用于单行的地址。一旦找到和address 匹配的行,那么脚本就结束 。执行q命令之后,就不会再产生输出。
如果比较下面的两个shell脚本,就会发现第一个脚本比第二个要运行得更好。下面这个简单的shell程序打印文件的前10行,然后退出:
for file
do
sed 10q $file
done
下一个示例也打印前10行,它采用打印命令并抑制默认的输出:
for file
do
sed -n 1, 10p $file
done
高级命令分成了3个组:
在前面正则表达式的讨论中,我们强调模式匹配是面向行的。像grep这样的程序尝试在单个输入行上匹配一个模式。这就使它很难匹配一个在一行的结尾处开始。并在下一行的开始处结束的短语。
sed 能查看模式空间的多个行。这就是允许匹配模式扩展到多行上。这里的3个多行命令(N、 D、P)对应于小写字母的基本命令(n、 d、p)。例如,删除命令(D)是删除命令(d) 的多行形式。区别是: d 删除模式空间的内容,D只删除多行模式空间的第一行。
多行Next (N)命令通过读取新的输入行,并将它添加到模式空间的现有内容之后来创建多行模式空间。模式空间最初的内容和新的输入行之间用换行符分隔。在模式空间中嵌入的换行符可以利用转义序列“\n”来匹配。在多行模式空间中,元字符“^”匹配空间中的第一个字条,而不匹配换行符后面的字符。同样,“$” 只匹配模式空间中最后的换行符,而不匹配任何嵌入的换行符。Next命令与next命令不同,next 会读取新的输入行,next命令不创建多行模式空间。
记住,不一定要替换换行符,但是如果不替换它将会生成一些很长的行。
# 替换后,读取第二行模式空间的内容时,已经改变
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC' | sed 'N; s/\n/,/p'
AAA,BBB
AAA,BBB
CCC
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC' | sed -n 'N; s/\n/,/p'
AAA,BBB
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC' | sed -n 'N; s/\n/,/p; s/AAA/UFO-1\nUFO-2\nUFO-3/p'
AAA,BBB
UFO-1
UFO-2
UFO-3,BBB
需要注意的是,N 命令并不会自动打印任何内容,如果你想将处理后的行打印出来,可以加上 p 命令。
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC' | sed -n '$!N; s/\n/,/p;p;s/CCC/UFO-111/p'
AAA,BBB
AAA,BBB
CCC
UFO-111
注意,$!N 排除了对最后一行执行 Next 命令。
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC' | sed -n 'N;N;s/\n/,/gp'
AAA,BBB,CCC
# 每3行合并成1行,不足三行时,直接忽略。
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\n' | sed -n 'N;N;s/\n/,/gp'
AAA,BBB,CCC
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\nDDD\nEEE' | sed -n 'N;N;s/\n/,/gp'
AAA,BBB,CCC
# 每3行合并成1行,不足三行时,不忽略。不能用行号直接匹配倒数第n行。
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\nDDD\nEEE' | sed -n 'N;$!N;s/\n/,/gp'
AAA,BBB,CCC
DDD,EEE
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC\nDDD\nEEE' | sed -n '$!N;$!N;s/\n/,/gp'
AAA,BBB,CCC
DDD,EEE
删除命令(d) 删除模式空间的内容并导致读入新的输入行,从而在脚本的顶端重新使用编辑方法。删除命令(D) 稍微有些不同:它删除模式空间中直到第一个嵌入的换行符的这部分内容。它不会导致读入新的输入行,相反,它返回到脚本的顶端,将这些指令应用于模式空间剩余的内容,我们可以编写一个实现查找一系列空行并输出单个空行的脚本,以看看它们之间的区别。
下面的语句使用了删除命令(d) :
# 将多个空行减少到一行,利用 d 命令的版本。
[root@ufo666 ~]# echo -e 'AAA\n\n\nBBB'
AAA
BBB
[root@ufo666 ~]# echo -e 'AAA\n\n\nBBB' | sed '/^$/{N;/^\n$/d}'
AAA
BBB
[root@ufo666 ~]# echo -e 'AAA\n\n\nBBB' | sed '/^$/{N;/^\n$/D}'
AAA
BBB
多行打印(Print) 命令与小写字母的print命令稍有不同。该命令输出多行模式空间的第一部分,直到第一个嵌入的换行符为止。在执行完脚本的最后一个命令之后,模式空间的内容自动输出( -n 选项或 #n 抑制这个默认的动作)。因此,当默认的输出被抑制或者脚本中的控制流更改,以至不能到达脚本的底部时,需要使用打印命令(P或p), Print 命令经常出现在Next命令之后和Delete命令之前。这3个命令能建立一一个输入/输出循环,用来维护两行的模式空间,但是一次只输出一行。这个循环的目的是只输出模式空间的第一行,然后返回到脚本的顶端将所有的命令应用于模式空间的第二行。没有这个循环,当执行脚本中的最后一个命令时,模式空间中的这两行都将被输出。
# AAA --> AAA\nBBB --> p打印全部 --> 666\nBBB --> 打印666, 删除模式空间666
# BBB --> BBB\nCCC --> p打印全部 --> 不匹配,无后续输出
# CCC --> 不匹配,无后续输出
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC' | sed -n '/A/,/B/{N;p;/AAA\n/{s/AAA\n/666\n/;P;D}}'
AAA
BBB
666
BBB
CCC
注意,改变的是多行模式空间的 第一行 还是 第二行,影响后续的输入情况。
模式空间是容纳当前输入行的缓冲区。还有一个称为保持空间(hold space)的顶留(set-aside) 缓冲区。模式空间的内容可以复制到保持空间,而且保持空间的内容也可以复制到模式空间。有一组命令用于在保持空间和模式空间之间移动数据。保持空间用于临时存储。单独的命令不能寻址保持空间或者更改它的内容。
保持空间最常的用途是,当改变模式空间中的原始内容时,用于保留当前输入行的副本。影响模式空间的命令有:
这些命令中的每一条都可以利用一个地址来指定一行或行范围。Hole(h,H)命令将数据移至保持空间、而get (g,G) 命令将保持空间的数据移回到模式空间。同一命令的小写字母和大写字母之间的差别是,小写字母命令改写目的缓存区的内容,而大写字母命令追加缓存区的现有内容。
Hold命令用模式空间的内容取代保持空间的内容。Get命令用保持空间的内容取代模式空间的内容。
Hole命令在保持空间的内容之后放置一个换行符,且后面跟随模式空间的内容(即使保持空间是空的,换行符也被追加到保持空间中)。Get命令在模式空间的内容之后放置一个换行符,且后面跟随保持空间的内容。
交换命令交换两个缓存区的内容,对两个缓存区没有副作用。需要注意的是,这些命令在处理文本时会改变模式空间和保持空间的内容,因此在实际应用中请确保正确地使用和理解这些命令的作用。
[root@ufo666 ~]# echo -e "name:赵先生\nage:20\n北京\n\nname:李先生\nage:25\n上海"
name:赵先生
age:20
北京
name:李先生
age:25
上海
# 多输入几行数据,一起处理后返回
[root@ufo666 ~]# echo -e "name:赵先生\nage:20\ncity:北京\n\nname:李先生\nage:25\ncity:上海" | sed -n '/name:/{h};/city:/{H;x;s/\n/ - /p}'
name:赵先生 - city:北京
name:李先生 - city:上海
# 复制上一行数据后变多行模式,一起处理后返回
[root@ufo666 ~]# echo -e 'AAA\nBBB\nCCC' | sed -n 'h; G; s/\n/ - /p'
AAA - AAA
BBB - BBB
CCC - CCC
分支(b)和测式 (t)命令将脚本中的控制转移到包含特殊标签的行。如果没有指定标签,则将控制转移到脚本的结尾处。分支命令(b)用于无条件转移;测式命令(t)用于有条件转移,只有当替换命令改变当前行时才会执行。
在冒号和标签之间不允许有空格。行结尾处的空格将被认为是标签的一部分。当在分支命令或测试命令中指定标签时,在命令和标签之间允许有空格:
b mylabel
t mylabel
标签是任意不多于7个字符的序列。标签本身占据一行并以冒号开始:
:mylabel
注意:不要在标签后面插入空格。
Branch 命令用于在脚本中将控制流转移到另一行。
[address]b[label]
Label 是可选的,如果没有给出 label,控制就被转到脚本的结尾处。如果有 label 就继续执行标签后面的行。
分支命令可用于将一组命令作为一个过程来执行,这个过程可以从这个脚本的主体中重复调用。也可用于避免执行某个基于一个模式匹配的过程。通过使用(!)并组合一组命令可以实现类似的效果。在应用中对分支命令使用(!)的优点是,可以更容易地指定要避免的多个情况。(!)符号可以应用于单个命令,或者应用于紧随其后的包围在大括号中的一组命令。另一方面,分支命令赋予了你几乎不受限制的脚本跳转控制能力。
[root@ufo666 ~]# echo -e 'AAA\nAAAsed\nCCC\nDDD' | sed -n ': skip; /AAA/{/sed/!{s/A/666/gp}}; : skip; p'
666666666
666666666
AAAsed
CCC
DDD
[root@ufo666 ~]# echo -e 'AAA\nAAAsed\nCCC\nDDD' | sed -n ': skip; /AAA/{/sed/!{s/A/666/gp}}; : skip'
666666666
[root@ufo666 ~]# echo -e 'AAA\nAAAsed\nCCC\nDDD' | sed -n ': skip; /AAA/{/sed/!{s/A/666/gp}}'
666666666
[root@ufo666 ~]# echo -e 'AAA\nAAAsed\nCCC\nDDD' | sed -n ': skip; /AAA/{/sed/{s/A/666/gp}}; : skip;p'
AAA
666666666sed
666666666sed
CCC
DDD
# 直接跳过 /AAA/,/sed/ 范围内行,执行后续命令
[root@ufo666 ~]# echo -e 'AAA\nAAAsed\nCCC\nDDD' | sed -n '/AAA/,/sed/b;p'
CCC
DDD
# b单独成一行时,直接跳转到结束,下边示例并不会打印多次
[root@ufo666 ~]# echo -e 'AAA\nAAAsed\nCCC\nDDD' | sed -n '/AAA/,/sed/b;p;b;p'
CCC
DDD
[root@ufo666 ~]# echo -e 'AAA\nAAAsed\nCCC\nDDD' | sed -n '/AAA/,/sed/b;p;b;p;p;p'
CCC
DDD
# 循环
:top
command1
command2
/pattern/b top
command3
# 跳过 command2
command1
/pattern/b end
command2
:end
command3
# 选择分支
command1
/pattern/b dothree
command2
b
:dothree
command3
如果在当前匹配地址的行上进行了成功的替换,那么 test命令就会转到标签 (或者脚本的结尾)处。因此,它隐含了一个条件分支。Test 命令语法如下:
[address]t[label]
如果没有给出标签 label,控制被转移支脚本的结尾处。如果提供了标签 label,那么就会继续执行标签后面的行。
[root@ufo666 ~]# echo -e 'AAA\nAAAsed\nCCC\nDDD' | sed -n 's/A/666/;t;p;p'
CCC
CCC
DDD
DDD
[root@ufo666 ~]# echo -e 'AAA\nAAAsed\nCCC\nDDD' | sed -n 's/h/666/;t;p;p'
AAA
AAA
AAAsed
AAAsed
CCC
CCC
DDD
DDD
s/foo/bar/
t mylabel
3,5 t mylabel
t
2,$ t end
至此,所有的 sed 命令已经介绍完。
附录一【sed命令快速参考】
https://blog.csdn.net/qq_42226855/article/details/135087772
附录二【awk命令快速参考】
https://blog.csdn.net/qq_42226855/article/details/135088023