请选择 进入手机版 | 继续访问电脑版
查看: 114|回复: 4

[经验分享] 基于sed常用命令的介绍(2)

[复制链接]

签到天数: 104 天

[LV.6]签到达人

发表于 2018-9-30 16:37:11 | 显示全部楼层 |阅读模式
承接上一篇帖子 ,继续介绍几个sed常用的命令。
(6).手动读取下一行命令"n"。

在sed的循环过程中,每个sed循环的第一步都是读取输入流的下一行到模式空间中,这是我们无法控制的动作。但sed有读取下一行的命令"n"。

由于是读取下一行,所以它会触发自动输出的动作,于是就有了输出流。不仅如此,还应该记住的是:只要有读取下一行的行为,在其真正开始读取之前一定有隐式自动输出的行为。

但需注意,当没有下一行可供"n"读取时(例如文件的最后一行已经被读取过了),将输出模式空间内容后直接退出sed程序,使得"n"命令后的所有命令都不会执行,即使是那两个隐含动作。

相应的循环结构如下:

for ((line=1;line<=last_line_num;++line))
do
    read $line to pattern_space;
    while pattern_space is not null
    do
        execute cmd1 in SCRIPT;
        execute cmd2 in SCRIPT;
        ADDR1,ADDR2{              # "n" command
            if [ "$line" -ne "$last_line_num" ];then
                auto_print;
                remove_pattern_space;
                read next_line to pattern_space;
            else
                auto_print;
                remove_pattern_space;
                exit;
            fi
        };
        ……
        auto_print;
        remove_pattern_space;
    done
done
注意,是先判断是否有下一行可读取,再输出和清空pattern space中的内容,所以then和else语句中都有这两个动作。 也许感觉上似乎更应该像下面这样的优化形式:

ADDR1,ADDR2{              # "n" command
         auto_print;
         remove_pattern_space;
         [ "$line" -ne "$last_line_num" ] && read next_line to pattern_space || exit;
};
但事实证明并非如此,证明过程在本文结尾。此处暂不讨论这些复杂的东西,先看看"n"命令的示例。

例如,搜索a.txt中包含"redirect"字符串的行以及其下一行,并输出。

sed -n '/redirect/{p;n;p}' a.txt
再例如下面的命令。

echo -e "abc\ndef\nxyz" | sed '/abc/{n;=;p}'
abc
2
def
def
xyz
从结果中可以分析出,"n"读取下一行前输出了"abc",然后立即读入了下一行,所以输出的行号是2而不是1,因为这时候行号计数器已经读取了下一行,随后命令"p"输出了该模式空间的内容,输出后还有一次自动输出的隐含动作,所以"def"被输出了两次。

(7).替换命令"s"。

这是sed用的最多的命令。两个字就能概括其功能:替换。将匹配到的内容替换成指定的内容。

"s"命令的语法格式为:其中"/"可以替换成任意其他单个字符。

s/REGEXP/REPLACEMENT/FLAGS
它使用REGEXP去匹配行,将匹配到的那部分字符替换成REPLACEMENT。FLAGS是"s"命令的修饰符,常见的有"g"、"p"和"i"或"I"。

"g":表示替换行中所有能被REGEXP匹配的部分。不使用g时,默认只替换行中的第一个匹配内容。此外,"g"还可以替换成一个数值N,表示只替换行中第N个被匹配的内容。

"p":输出替换后模式空间中的内容。

"i"或"I":REGEXP匹配时不区分大小写。

REPLACEMENT中可以使用"\N"(N是从1到9的整数)进行后向引用,所代表的是REGEXP第N个括号(...)中匹配的内容。另外,REPLACEMENT中可以包含未转义的"&"符号,这表示引用pattern space中被匹配的整个内容。需要注意,"&"是引用pattern space中的所有匹配,不仅仅只是括号的分组匹配。

例如,删除a.sh中所有"#"开头(可以包括前导空白)的注释符号"#",但第一行"#!/bin/bash"不处理。

sed -i '2,$s/^[ \t]*#//' a.sh
为a.sh文件中的第5行到最后一行的行首加上注释符号"#"。

sed '5,$s/^/#/' a.sh
将a.sh中所有的"int"单词替换成"SIGINT"。

sed 's/\bint\b/SIGINT/g' a.sh
将a.sh中"cmd1 && cmd2 || cmd3"的cmd2和cmd3命令对调个位置。

sed 's%&&\(.*\) ||\(.*\)%\&\&\2 ||\1%' a.sh  
这里使用了"%"代替"/",且在REPLACEMENT部分对"&"进行了转义,因为该符号在REPLACEMENT中时表示的是引用REGEXP所匹配的所有内容。

(8).追加、插入和修改命令"a"、"i"、"c"。

这3个命令的格式是"[a|i|c] TEXT",表示将TEXT内容队列化到内存中,当有输出流或者说有输出动作的时候,半路追上输出流,分别追加、插入和替换到该输出流然后输出。追加是指追加在输出流的尾部,插入是指插入在输出流的首部,替换是指将整个输出流替换掉。"c"命令和"a"、"i"命令有一丝不同,它替换结束后立即退出当前SCRIPT循环,并进入下一个sed循环,因此"c"命令后的命令都不会被执行。

例如:

echo -e "abc\ndef" | sed '/abc/a xyz'
abc
xyz
def
其实"a"、"i"和"c"命令的TEXT部分写法是比较复杂的,如果TEXT只是几个简单字符,如上即可。但如果要TEXT是分行文本,或者包含了引号,或者这几个命令是写在"{}"中的,则上面的写法就无法实现。需要使用符号"\"来转义行尾符号,这表示开启一个新行,此后输入的内容都是TEXT,直到遇到引号或者";"开头的行时。

例如,在a.sh的#!/bin/bash行后添加一个注释行"# Script filename: a.sh"以及一个空行。由于是追加在尾部,所以使用"a"命令。

sed '\%#!/bin/bash%a\# Script filename: a.sh\n' a.sh
"a"命令后的第一个反斜线用于标记TEXT的开始,"\n"用于添加空白行。如果分行写,或者"a"命令写在大括号"{}"中,则格式如下:

sed '\%#!/bin/bash%a\
# Script filename: a.sh\n
' a.sh

sed '\%#!/bin/bash%{p;a\
# Script filename: a.sh\n
;p}' a.sh
最后需要说的是,这3个命令的TEXT是存放在内存中的,不会进入模式空间,因此不受"-n"选项或某些命令的影响。此外,这3个命令依赖于输出流,只要有输出动作,不管是空输出流还是非空的输出流,只要有输出,这几个命令就会半路"劫杀"。如果不理解这两句话,这3个命令的结果有时可能会比较疑惑。

例如,"a"命令是追加在当前匹配行行尾的,但为什么下面的"haha"却插入到匹配行"def"的前面去了呢?

echo -e "abc\ndef\nxyz" | sed '/def/{a\
haha
;N}'

abc
haha
def
xyz
阅读了下面的"N"命令之后,再回头看这个示例,应该能知道为什么。在sed修炼系列(四):sed中的疑难杂症中给出了解释。

(9).多行模式命令"N"、"D"、"P"简单说明。

在前面已经解释了"n"、"d"和"p"命令,sed还支持它们的大写命令"N"、"D"和"P"。

"N"命令:读取下一行内容追加到模式空间的尾部。其和"n"命令不同之处在于:"n"命令会输出模式空间的内容(除非使用了"-n"选项)并清空模式空间,然后才读取下一行到模式空间,也就是说"n"命令虽然读取了下一行到模式空间,但模式空间仍然是单行数据。而"N"命令在读取下一行前,虽然也有自动输出和清空模式空间的动作,但该命令会把当前模式空间的内容锁住,使得自动输出的内容为空,也无法清空模式空间,然后读取下一行追加到当前模式空间中的尾部。追加时,原有内容和新读取内容使用换行符"\n"分隔,这样在模式空间中就实现了多行数据。即所谓的"多行模式"。 另外,当无法读取到下一行时(到了文件尾部),将直接退出sed程序,使得"N"命令后的命令不会再执行,这和"n"命令是一样的。

"D"命令:删除模式空间中第一个换行符"\n"之前的内容,然后立即回到SCRIPT循环的顶端,即进入下一个SCRIPT循环。如果"D"删除后,模式空间中已经没有内容了,则SCRIPT循环自动退出进入下一个sed循环;如果模式空间还有剩余内容,则继续从头执行SCRIPT循环。也就是说,"D"命令后的命令不会被执行。

"P"命令:输出模式空间中第一个换行符"\n"之前的内容。

"N"、"D"和"P"命令作用非常大,它们是绝佳的组合命令,因为借助它们能实现"窗口滑动"技术,这对于复杂的文本行操作来说大有裨益。但显然,这不是本文的内容,在sed修炼系列(三):sed高级应用之实现窗口滑动技术中详细说明了这3个命令的功能。

此处按照惯例,还是给出它们的大致循环结构:其中"N"命令的if判断和前文的"n"一样,在本文结尾证明。

# "N"命令的大致循环结构
for ((line=1;line<=last_line_num;++line))
do
    read $line to pattern_space;
    while pattern_space is not null
    do
        execute cmd1 in SCRIPT;
        execute cmd2 in SCRIPT;
        ADDR1,ADDR2{           # "N" command
            if [ "$line" -ne "$last_line_num" ];then
                lock pattern_space;
                auto_print;
                remove_pattern_space;
                unlock pattern_space;
                append "\n" to pattern_space;
                read next_line to pattern_space;
            else
                auto_print;
                remove_pattern_space;                  
                exit;
            fi
        };
        ……
        auto_print;
        remove_pattern_space;
    done
done

# "D"命令的大致循环结构
for ((line=1;line<=last_line_num;++line))
do
    read $line to pattern_space;
    while pattern_space is not null
    do
        execute cmd1 in SCRIPT;
        execute cmd2 in SCRIPT;
        ADDR1,ADDR2{               # "D" command
            delete first line in pattern_space;
            continue;
        };
        ……
        auto_print;
        remove_pattern_space;
    done
done

# "P"命令的大致循环结构
for ((line=1;line<=last_line_num;++line))
do
    read $line to pattern_space;
    while pattern_space is not null
    do
        execute cmd1 in SCRIPT;
        execute cmd2 in SCRIPT;
        ADDR1,ADDR2{               # "P" command
            print first line in pattern_space;
        };
        ……
        auto_print;
        remove_pattern_space;
    done
done
(10).buffer空间数据交换命令"h"、"H"、"g"、"G"、"x"简单说明。

sed除了维护模式空间(pattern space),还维护另一个buffer空间:保持空间(hold space)。这两个空间初始状态都是空的。

绝大多数时候,sed仅依靠模式空间就能达到目的,但有些复杂的数据操作则只能借助保持空间来实现。之所以称之为保持空间,是因为它是暂存数据用的,除了仅有的这几个命令外,没有任何其他命令可以操作该空间,因此借助它能实现数据的持久性。

保持空间的作用很大,它和模式空间之间的数据交换能实现很多看上去不能实现的功能,是实现sed高级功能所必须的,例如"窗口滑动"。同样,这不是本文的内容。所以只简单解释这几个命令的作用:

"h"命令:将当前模式空间中的内容覆盖到保持空间。

"H"命令:在保持空间的尾部加上一个换行符"\n",并将当前模式空间的内容追加到保持空间的尾部。

"g"命令:将保持空间的内容覆盖到当前模式空间。

"G"命令:在模式空间的尾部加上一个换行符"\n",并将当前保持空间的内容追加到模式空间的尾部。

"x"命令:交换模式空间和保持空间的内容。

注意,无论是交换、追加还是覆盖,原空间的内容都不会被删除。

签到天数: 673 天

[LV.9]元老将成

发表于 2018-10-8 08:31:34 | 显示全部楼层
不错的资料
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

返回顶部