流编辑器sed和gawk

sed编辑器是一种流编辑器,区别于交互式vim编辑器,处理数据更快。
注意:sed编辑器并不会修改文本文件的数据。它只会将修改后的数据发送到STDOUT!
sed 命令的格式如下:
    sed options script file  (sed 选项 脚本 文件)
sed 命令选项:
    选 项  描 述
    -e script   在处理输入时,将 script 中指定的命令添加到已有的命令中
    -f file     在处理输入时,将 file 中指定的命令添加到已有的命令中
    -n          不产生命令输出,使用 print 命令来完成输出
1. 在命令行定义编辑器命令
    echo "This is a test" | sed 's/test/big test/'    -- This is a big test  s命令会用斜线间指定的第二个文本字符串来替换第一个文本字符串模式。在本例中是 big test 替换了 test 
    sed 's/dog/cat/' data1.txt    -- 将data1.txt中的dog替换成cat
2. 在命令行使用多个编辑器命令( -e 选项)
    sed -e 's/brown/green/; s/dog/cat/' data1.txt    -- The quick green fox jumps over the lazy cat.
    $ sed -e '    -- 不用分号分隔
    > s/brown/green/
    > s/fox/elephant/
    > s/dog/cat/' data1.txt
3. 从文件中读取编辑器命令( -f 选项)
    $ cat script1.sed
    s/brown/green/
    s/fox/elephant/
    s/dog/cat/
    $
    $ sed -f script1.sed data1.txt
    The quick green elephant jumps over the lazy cat.
    The quick green elephant jumps over the lazy cat.
    The quick green elephant jumps over the lazy cat.
    The quick green elephant jumps over the lazy cat.
    $
    
gawk编辑器
gawk 命令的格式如下:
    gawk options program file
gawk选项:
    选 项            描 述
    -F fs         指定行中划分数据字段的字段分隔符
    -f file       从指定的文件中读取程序
    -v var=value  定义gawk程序中的一个变量及其默认值
    -mf N         指定要处理的数据文件中的最大字段数
    -mr N         指定数据文件中的最大数据行数
    -W keyword    指定gawk的兼容模式或警告等级
1. 从命令行读取程序脚本
    脚本命令需要放入{}中。由于 gawk 命令行假定脚本是单个文本字符串,你还必须将脚本放到单引号中
    gawk '{print "Hello World!"}'    -- 无论输入什么回车都是Hello World
2.使用数据字段变量
    $0 代表整个文本行;
    $1 代表文本行中的第1个数据字段;
    $2 代表文本行中的第2个数据字段;
    $n 代表文本行中的第n个数据字段。
    如:
    $ cat data2.txt    -- gawk中默认的字段分隔符是任意的空白字符(例如空格或制表符)
    One line of test text.
    Two lines of test text.
    Three lines of test text.
    $
    $ gawk '{print $1}' data2.txt
    One
    Two
    Three
    $
    gawk -F: '{print $1}' /etc/passwd    -- 如果你要读取采用了其他字段分隔符的文件,可以用 -F 选项指定,-F:并以冒号作为分隔符
3.在程序脚本中使用多个命令
    echo "My name is Rich" | gawk '{$4="Christine"; print $0}'  -- My name is Christine
    $ gawk '{    -- 也可以逐次输入
    > $4="Christine"
    > print $0}'
    My name is Rich
    My name is Christine
    $
4.从文件中读取程序( -f 选项指定文件)
    $ cat script2.gawk
    {print $1 "'s home directory is " $6}
    $
    $ gawk -F: -f script2.gawk /etc/passwd
    root's home directory is /root
    bin's home directory is /bin
    daemon's home directory is /sbin
    adm's home directory is /var/adm
    lp's home directory is /var/spool/lpd
    [...]
    Christine's home directory is /home/Christine
    Samantha's home directory is /home/Samantha
    Timothy's home directory is /home/Timothy
    $
    $ cat script3.gawk    -- 可以在程序文件中指定多条命令。只要一条命令放一行即可,不需要用分号
    {
    text = "'s home directory is "
    print $1 text $6    -- 注意,gawk程序在引用变量值时并未像shell脚本一样使用美元符
    }
    $
    $ gawk -F: -f script3.gawk /etc/passwd
    root's home directory is /root
    bin's home directory is /bin
    daemon's home directory is /sbin
    adm's home directory is /var/adm
    lp's home directory is /var/spool/lpd
    [...]
    Christine's home directory is /home/Christine
    Samantha's home directory is /home/Samantha
    Timothy's home directory is /home/Timothy
    $
5.在处理数据前运行脚本(begin)
    $ cat data3.txt
    Line 1
    Line 2
    Line 3
    $
    $ gawk 'BEGIN {print "The data3 File Contents:"}
    > {print $0}' data3.txt
    The data3 File Contents:
    Line 1
    Line 2
    Line 3
    $
6.在处理数据后运行脚本(end)
    $ gawk 'BEGIN {print "The data3 File Contents:"}
    > {print $0}
    > END {print "End of File"}' data3.txt
    $ cat script4.gawk
    BEGIN {
    print "The latest list of users and shells"
    print " UserID \t Shell"
    print "-------- \t -------"
    FS=":"
    }
    {
    print $1 " \t " $7
    }
    END {
    print "This concludes the listing"
    }
    $
    $ gawk -f script4.gawk /etc/passwd
#sed编辑器基础-------------------------
一、替换选项(s)
    1.替换标记格式:s/pattern/replacement/flags
        有4种可用的替换标记:
            数字,表明新文本将替换第几处模式匹配的地方;
            g ,表明新文本将会替换所有匹配的文本;
            p ,表明原先行的内容要打印出来;
            w file ,将替换的结果写到文件中。
        测试:
            $ cat data4.txt
            This is a test of the test script.
            This is a different line.
            $
            $ sed 's/test/trial/' data4.txt
            This is a trial of the test script.    -- 默认情况下它只替换每行中出现的第一处test
            This is the second trial of the test script.
            $
            $ sed 's/test/trial/2' data4.txt    -- 替换了第二处的test
            This is a test of the trial script.
            This is the second test of the trial script.
            $
            $ sed 's/test/trial/g' data4.txt    -- 替换了所有test
            This is a trial of the trial script.
            This is the second trial of the trial script.
            $
            $ sed -n 's/test/trial/p' data5.txt    -- -n 选项将禁止sed编辑器输出。但 p 替换标记会输出修改过的行。将二者配合使用的效果就是只输出被替换命令修改过的行。
            This is a trial line.
            $
            $ sed 's/test/trial/w test.txt' data5.txt    -- 更改过的行将再输出到文件test.txt
            This is a trial line.
            This is a different line.
            $
            $ cat test.txt
            This is a trial line.
            $
    2.替换字符
        $ sed 's/\/bin\/bash/\/bin\/csh/' /etc/passwd    -- 由于正斜线通常用作字符串分隔符,因而如果它出现在了模式文本中的话,必须用反斜线来转义。麻烦!可以使用下面的方式。
        $ sed 's!/bin/bash!/bin/csh!' /etc/passwd    -- 感叹号被用作字符串分隔符
二、使用地址
    1. 数字方式的行寻址
        sed '2s/dog/cat/' data1.txt      -- 只改变第二行
        sed '2,3s/dog/cat/' data1.txt    -- 改变第2到3行这个区间的[2,3]
        sed '2,$s/dog/cat/' data1.txt    -- 改变第2到末尾这个区间[2,$]
    2. 使用文本模式过滤器
        $ grep Samantha /etc/passwd
        Samantha:x:502:502::/home/Samantha:/bin/bash
        $
        $ sed '/Samantha/s/bash/csh/' /etc/passwd   -- 必须用正斜线将要指定的 pattern 封起来
        [...]
        Samantha:x:502:502::/home/Samantha:/bin/csh
        [...]
        $
    3. 命令组合
        $ sed '2{    -- 单行上执行多条命令,可以用花括号将多条命令组合在一起
        > s/fox/elephant/
        > s/dog/cat/
        > }' data1.txt
        The quick brown fox jumps over the lazy dog.
        The quick brown elephant jumps over the lazy cat.
        The quick brown fox jumps over the lazy dog.
        The quick brown fox jumps over the lazy dog.
        $
三、删除行(d)
        $ cat data1.txt
        The quick brown fox jumps over the lazy dog
        The quick brown fox jumps over the lazy dog
        The quick brown fox jumps over the lazy dog
        The quick brown fox jumps over the lazy dog
        $
        $ sed 'd' data1.txt  -- 删除所有行
        $
        $ sed '3d' data6.txt -- 删除第三行
        $
        $ sed '2,3d' data6.txt  -- 删除2至3行
        $
        $ sed '/number 1/d' data6.txt  -- 删除‘number 1’的行
        $
        $ sed '/number 1/,/number 3/d' data7.txt  -- ‘number 1’开启删除,‘number 3’结束删除。如果后面出现‘number 1’将再次开启,若未找到‘number 3’结束行将会删除之后所有
        $
四、插入和附加文本
    插入( insert )命令( i )会在指定行前增加一个新行;
    附加( append )命令( a )会在指定行后增加一个新行。
        $ echo "Test Line 2" | sed 'i\Test Line 1'    -- 当使用插入命令时,文本会出现在数据流文本的前面
        Test Line 1    
        Test Line 2    
        $ echo "Test Line 2" | sed 'a\Test Line 1'    -- 当使用附加命令时,文本会出现在数据流文本的后面
        Test Line 2
        Test Line 1
        $ sed '3i\
        > This is an inserted line.' data6.txt    -- 将一个新行插入到数据流第三行前
        This is line number 1.
        This is line number 2.
        This is an inserted line.
        This is line number 3.
        This is line number 4.
        $ sed '3a\
        > This is an appended line.' data6.txt    -- 将一个新行附加到数据流中第三行后
        This is line number 1.
        This is line number 2.
        This is line number 3.
        This is an appended line.
        This is line number 4.
        $ sed '$a\
        > This is a new line of text.' data6.txt    -- 将一个新行附加到数据流末
        This is line number 1.
        This is line number 2.
        This is line number 3.
        This is line number 4.
        This is a new line of text.
五、修改行
    修改( change )命令允许修改数据流中整行文本的内容。它跟插入和附加命令的工作机制一样。
        $ sed '3c\
        > This is a changed line of text.' data6.txt    -- 修改第三行
        This is line number 1.
        This is line number 2.
        This is a changed line of text.
        This is line number 4.
        $ sed '/number 3/c\
        > This is a changed line of text.' data6.txt    -- 修改匹配模式的行
        This is line number 1.
        This is line number 2.
        This is a changed line of text.
        This is line number 4.
        $ sed '2,3c\
        > This is a new line of text.' data6.txt    -- 修改区间行,结果不如所愿。替换了数据流中的两行文本
        This is line number 1.
        This is a new line of text.
        This is line number 4.
六、转换命令
    转换( transform )命令( y )是唯一可以处理单个字符的sed编辑器命令。
    格式:sed y/inchars/outchars/  转换命令会对 inchars 和 outchars 值进行一对一的映射,所以长度要等同。 inchars 中的第一个字符会被转换为 outchars 中的第一个字符,第二个字符会被转换成 outchars 中的第二个字符
        $ echo "This 1 is a test of 1 try." | sed 'y/123/456/'    
        This 4 is a test of 4 try.    -- 1转换成了4
        $
七、打印
    p 命令用来打印文本行;
    等号( = )命令用来打印行号;
    l (小写的L)命令用来列出行。
    1. 打印行
        $ echo "this is a test" | sed 'p'    -- 打印输出的一行
        this is a test
        this is a test
        $
        $ cat data6.txt
        This is line number 1.
        This is line number 2.
        This is line number 3.
        This is line number 4.
        $ sed -n '/number 3/p' data6.txt    -- 打印命令最常见的用法是打印包含匹配文本模式的行
        This is line number 3.
        $
        $ sed -n '2,3p' data6.txt    -- 打印[2,3]行
        This is line number 2.
        This is line number 3.
        $
        $ sed -n '/3/{    -- sed编辑器命令会查找包含数字3的行,然后执行两条命令。首先,脚本用 p 命令来打印出原始行;然后它用 s 命令替换文本,并用 p 标记打印出替换结果
        > p
        > s/line/test/p
        > }' data6.txt
        This is line number 3.
        This is test number 3.
        $
    2. 打印行号
    等号命令会打印行在数据流中的当前行号
        $ cat data1.txt
        The quick brown fox jumps over the lazy dog.
        The quick brown fox jumps over the lazy dog.
        The quick brown fox jumps over the lazy dog.
        The quick brown fox jumps over the lazy dog.
        $
        $ sed '=' data1.txt
        1
        The quick brown fox jumps over the lazy dog.
        2
        The quick brown fox jumps over the lazy dog.
        3
        The quick brown fox jumps over the lazy dog.
        4
        The quick brown fox jumps over the lazy dog.
        $
        $ sed -n '/number 4/{    -- 利用 -n 选项,你就能让sed编辑器只显示包含匹配文本模式的行的行号和文本
        > =
        > p
        > }' data6.txt
        4
        This is line number 4.
        $
    3. 列出行
    列出( list )命令( l )可以打印数据流中的文本和不可打印的ASCII字符
        $ cat data9.txt
        This line contains tabs.
        $
        $ sed -n 'l' data9.txt
        This\tline\tcontains\ttabs.$    -- 制表符的位置会使用 \t 来显示。行尾的美元符表示换行符。
        $
        $ cat data10.txt
        This line contains an escape character.
        $
        $ sed -n 'l' data10.txt
        This line contains an escape character. \a$    -- data10.txt文本文件包含了一个转义控制码来产生铃声。当用 cat 命令来显示文本文件时,你看不到转义控制码,只能听到声音(如果你的音箱打开的话)。但是,利用列出命令,你就能显示出所使用的转义控制码
        $
八、使用 sed 处理文件
    1. 写入文件(write,w)
    格式:[address]w filename    -- filename 参数指定了数据文件的绝对路径或相对路径,address指的是外部文件data6.txt
        $ sed '1,2w test.txt' data6.txt    -- 区间使用。如果你不想让行显示到 STDOUT 上,你可以用 sed 命令的 -n 选项
        This is line number 1.
        This is line number 2.
        This is line number 3.
        This is line number 4.
        $
        $ cat test.txt
        This is line number 1.
        This is line number 2.
        $
        $ cat data11.txt
        Blum, R Browncoat
        McGuiness, A Alliance
        Bresnahan, C Browncoat
        Harken, C Alliance
        $
        $ sed -n '/Browncoat/w Browncoats.txt' data11.txt    -- 模式使用。
        $
        $ cat Browncoats.txt
        Blum, R Browncoat
        Bresnahan, C Browncoat
        $
    2. 从文件读取数据(read,r)
    格式:[address]r filename
        $ cat data12.txt
        This is an added line.
        This is the second added line.
        $
        $ sed '3r data12.txt' data6.txt    -- 第三行后
        This is line number 1.
        This is line number 2.
        This is line number 3.
        This is an added line.
        This is the second added line.
        This is line number 4.
        $
        $ sed '/number 2/r data12.txt' data6.txt
        This is line number 1.
        This is line number 2.
        This is an added line.
        This is the second added line.
        This is line number 3.
        This is line number 4.
        $
        $ sed '$r data12.txt' data6.txt
        This is line number 1.
        This is line number 2.
        This is line number 3.
        This is line number 4.
        This is an added line.
        This is the second added line.
        $
        $ cat notice.std
        Would the following people:
        LIST
        please report to the ship's captain.
        $
        $ sed '/LIST/{    -- 在list后添加,并删除list
        > r data11.txt
        > d
        > }' notice.std
        Would the following people:
        Blum, R Browncoat
        McGuiness, A Alliance
        Bresnahan, C Browncoat
        Harken, C Alliance
        please report to the ship's captain.
        $
        
sed深入
#多行命令
    需要对跨多行的数据执行特定操作。如一条短语分布在两行中。
    三个可用来处理多行文本的特殊命令:
        N :将数据流中的下一行加进来创建一个多行组(multiline group)来处理。
        D :删除多行组中的一行。
        P :打印多行组中的一行。
        next 命令
            1. 单行的 next 命令: n 命令会让sed编辑器移动到文本的下一行。
                $ cat data1.txt
                This is the header line.

                This is a data line.

                This is the last line.
                $                                
                $ sed '/header/{n ; d}' data1.txt    -- 找到header会删除模式匹配成功的下一行
                This is the header line.
                This is a data line.
                
                This is the last line.
                $
            2. 合并文本行:多行版本的 next 命令(用大写N)会将下一文本行添加到模式空间中已有的文本后,作为一行处理。
                $ cat data4.txt
                On Tuesday, the Linux System
                Administrator's group meeting will be held.
                All System Administrators should attend.
                $
                $ sed '
                > s/System Administrator/Desktop User/    -- 将单行命令放前面可以解决最后一行找不到下一行合并的情况
                > N
                > s/System\nAdministrator/Desktop\nUser/
                > ' data4.txt
                On Tuesday, the Linux Desktop
                User's group meeting will be held.
                All Desktop Users should attend.
                $
        多行删除命令
            sed编辑器提供了多行删除命令 D ,它只删除模式空间中的第一行。该命令会删除到换行符(含换行符)为止的所有字符
                $ sed 'N ; /System\nAdministrator/D' data4.txt
                Administrator's group meeting will be held.
                All System Administrators should attend.
                $ cat data5.txt

                This is the header line.
                This is a data line.

                This is the last line.
                $
                $ sed '/^$/{N ; /header/D}' data5.txt    -- 删除数据流中出现在第一行前的空白行
                This is the header line.
                This is a data line.

                This is the last line.
                $
        多行打印命令
            多行打印命令( P )沿用了同样的方法。它只打印多行模式空间中的第一行
                $ cat data3.txt
                On Tuesday, the Linux System
                Administrator's group meeting will be held.
                All System Administrators should attend.
                Thank you for your attendance.
                $
                $ sed -n 'N ; /System\nAdministrator/P' data3.txt
                On Tuesday, the Linux System
#保持空间
    模式空间(pattern space)是一块活跃的缓冲区,sed编辑器有另一块称作保持空间(hold space)的缓冲区域。
    sed编辑器的保持空间命令
        命 令  描 述
        h      将模式空间复制到保持空间
        H      将模式空间附加到保持空间
        g      将保持空间复制到模式空间
        G      将保持空间附加到模式空间
        x      交换模式空间和保持空间的内容
        $ cat data2.txt
        This is the header line.
        This is the first data line.
        This is the second data line.
        This is the last line.
        $
        $ sed -n '/first/ {h ; p ; n ; p ; g ; p }' data2.txt
        This is the first data line.
        This is the second data line.
        This is the first data line.
        $
        $ sed -n '/first/ {h ; n ; p ; g ; p }' data2.txt    -- 合理使用,以相反方向输出这两行
        This is the second data line.
        This is the first data line.
        $
#排除命令
    感叹号命令( ! )用来排除( negate )命令
        $ sed -n '/header/!p' data2.txt    -- 除了包含单词header那一行外,文件中其他所有的行都被打印出来了
        This is the first data line.
        This is the second data line.
        This is the last line.
        $
        $ sed '$!N;    -- 最后一行,不执行N命令
        > s/System\nAdministrator/Desktop\nUser/
        > s/System Administrator/Desktop User/
        > ' data4.txt
        On Tuesday, the Linux Desktop
        User's group meeting will be held.
        All Desktop Users should attend.
        $
        $ cat data2.txt
        This is the header line.
        This is the first data line.
        This is the second data line.
        This is the last line.
        $
        $ sed -n '{1!G ; h ; $p }' data2.txt    -- 倒序输出(未理解),可以使用tac达到相同效果cat data2.txt
        This is the last line.
        This is the second data line.
        This is the first data line.
        This is the header line.
        $
#改变流
    sed编辑器会从脚本的顶部开始,一直执行到脚本的结尾。sed编辑器提供了一个方法来改变命令脚本的执行流程
        分支( branch )命令 b的格式如下:
        [ address ]b [ label ]
        address 参数决定了哪些行的数据会触发分支命令。 label 参数定义了要跳转到的位置。如果没有加 label 参数,跳转命令会跳转到脚本的结尾。
            $ cat data2.txt
            This is the header line.
            This is the first data line.
            This is the second data line.
            This is the last line.
            $
            $ sed '{2,3b ; s/This is/Is this/ ; s/line./test?/}' data2.txt    -- 分支命令在数据流中的第2行和第3行处跳过了两个替换命令
            Is this the header test?
            This is the first data line.
            This is the second data line.
            Is this the last test?
            $    
            要是不想直接跳到脚本的结尾,可以为分支命令定义一个要跳转到的标签。标签以冒号开始,最多可以是7个字符长度。
            $ sed '{/first/b jump1 ; s/This is the/No jump on/    -- 自定义标签。标签允许你跳过地址匹配处的命令,但仍然执行脚本中的其他命令
            > :jump1    
            > s/This is the/Jump here on/}' data2.txt
            No jump on header line
            Jump here on first data line
            No jump on second data line
            No jump on last line
            $    
            $ echo "This, is, a, test, to, remove, commas." | sed -n '{
            > :start    -- 也可以跳转到脚本中靠前面的标签上,这样就达到了循环的效果
            > s/,//1p
            > /,/b start    -- 设置了匹配模式,没有(,)循环就会终止
            > }'
            This is, a, test, to, remove, commas.
            This is a, test, to, remove, commas.
            This is a test, to, remove, commas.
            This is a test to, remove, commas.
            This is a test to remove, commas.
            This is a test to remove commas.
            $    
        测试
            类似于分支命令,测试( test )命令( t )也可以用来改变sed编辑器脚本的执行流程
            格式:[ address ]t [ label ]    
                $ sed '{
                > s/first/matched/
                > t    -- first改行不再执行
                > s/This is the/No match on/
                > }' data2.txt
                No match on header line
                This is the matched data line
                No match on second data line
                No match on last line
                $    
                $ echo "This, is, a, test, to, remove, commas. " | sed -n '{
                > :start
                > s/,//1p
                > t start    -- 当无需替换时,测试命令不会跳转而是继续执行剩下的脚本
                > }'
                This is, a, test, to, remove, commas.
                This is a, test, to, remove, commas.
                This is a test, to, remove, commas.
                This is a test to, remove, commas.
                This is a test to remove, commas.
                This is a test to remove commas.
                $
#模式替代
    & 符号
        不管模式匹配的是什么样的文本,你都可以在替代模式中使用 & 符号来使用这段文本
        $ echo "The cat sleeps in his hat." | sed 's/.at/"&"/g'
        The "cat" sleeps in his "hat".
    替代单独的单词
        sed编辑器用圆括号来定义替换模式中的子模式。替代字符由反斜线和数字组成,第一个子模式分配字符 \1 ,给第二个子模式分配字符 \2 ,依此类推。
            $ echo "The System Administrator manual" | sed '
            > s/\(System\) Administrator/\1 User/'    -- \1 来提取第一个匹配的子模式
            The System User manual            
            $ echo "1234567" | sed '{    -- 大数字中插入逗号
            > :start
            > s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/
            > t start
            > }'
            1,234,567
#脚本中使用sed
    使用包装脚本
        $ cat sw_cs.sh
        sed -n '{ 1!G ; h ; $p }' $1
        $
        $ sh sw_cs.sh data2.txt
        This is the last line.
        This is the second data line.
        This is the first data line.
        This is the header line.
    重定向 sed 的输出
        $ cat fact.sh
        factorial=1
        counter=1
        number=$1
        #
        while [ $counter -le $number ]
        do
        factorial=$[ $factorial * $counter ]
        counter=$[ $counter + 1 ]
        done
        #
        result=$(echo $factorial | sed '{
        :start
        s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/
        t start
        }')
        #
        echo "The result is $result"
        #
        $
        $ sh fact.sh 20
        The result is 2,432,902,008,176,640,000
        $
#创建 sed 实用工具
    1.加倍行间距
        $ sed 'G' data2.txt
        This is the header line.
        
        This is the first data line.
        
        This is the second data line.
        
        This is the last line.
        
        $
        $ sed '$!G' data2.txt
        This is the header line.
        
        This is the first data line.
        
        This is the second data line.
        
        This is the last line.    -- 最后一行跳过G
        $
    2.对可能含有空白行的文件加倍行间距
        $ cat data6.txt
        This is line one.
        This is line two.

        This is line three.
        This is line four.
        $    
        $ sed '/^$/d ; $!G' data6.txt
        This is line one.

        This is line two.

        This is line three.

        This is line four.
        $    
    3.给文件中的行编号
        $ sed '=' data2.txt | sed 'N; s/\n/ /'
        1 This is the header line.
        2 This is the first data line.
        3 This is the second data line.
        4 This is the last line.
    4.打印末尾行(打印末尾10行)
        $ cat data7.txt
        This is line 1.
        This is line 2.
        This is line 3.
        This is line 4.
        This is line 5.
        This is line 6.
        This is line 7.
        This is line 8.
        This is line 9.
        This is line 10.
        This is line 11.
        This is line 12.
        This is line 13.
        This is line 14.
        This is line 15.
        $
        $ sed '{
        > :start
        > $q ; N ; 11,$D
        > b start
        > }' data7.txt
        This is line 6.
        This is line 7.
        This is line 8.
        This is line 9.
        This is line 10.
        This is line 11.
        This is line 12.
        This is line 13.
        This is line 14.
        This is line 15.
        $
    5.删除行
        删除连续的空白行:$ sed '/./,/^$/!d' data8.txt
        删除开头的空白行:$ sed '/./,$!d' data9.txt
        删除结尾的空白行:
            $ sed '{
            :start
            / ^\n*$/{$d; N; b start }
            }'
    6.删除 HTML 标签
        $ sed 's/<[^>]*>//g ; /^$/d' data11.txt

gawk深入
#使用变量
    一、内建变量
        1. 字段和记录分隔符变量
            gawk数据字段和记录变量
            变 量            描 述
            FIELDWIDTHS      由空格分隔的一列数字,定义了每个数据字段确切宽度
            FS               输入字段分隔符
            RS               输入记录分隔符
            OFS              输出字段分隔符
            ORS              输出记录分隔符
            FS与OPS的使用
                $ cat data1
                data11,data12,data13,data14,data15
                data21,data22,data23,data24,data25
                data31,data32,data33,data34,data35
                $ gawk 'BEGIN{FS=","} {print $1,$2,$3}' data1    -- 默认情况下,gawk将 OFS 设成一个空格
                data11 data12 data13
                data21 data22 data23
                data31 data32 data33
                $ gawk 'BEGIN{FS=","; OFS="-"} {print $1,$2,$3}' data1    -- 修改输出分隔符值
                data11-data12-data13
                data21-data22-data23
                data31-data32-data33
            FIELDWIDTHS 变量定义了四个字段,根据已定义好的字段长度来分割
                $ cat data1b
                1005.3247596.37
                115-2.349194.00
                05810.1298100.1
                $ gawk 'BEGIN{FIELDWIDTHS="3 5 2 5"}{print $1,$2,$3,$4}' data1b
                100 5.324 75 96.37
                115 -2.34 91 94.00
                058 10.12 98 100.1
            RS与ORS的使用:默认情况下,gawk将 RS 和 ORS 设为换行符
                $ cat data2
                Riley Mullen
                123 Main Street
                Chicago, IL 60601
                (312)555-1234
                
                Frank Williams
                456 Oak Street
                Indianapolis, IN 46201
                (317)555-9876
                
                Haley Snell
                4231 Elm Street
                Detroit, MI 48201
                (313)555-4938
                $ gawk 'BEGIN{FS="\n"; RS=""} {print $1,$4}' data2    -- 现在gawk把文件中的每行都当成一个字段,把空白行当作记录分隔符
                Riley Mullen (312)555-1234
                Frank Williams (317)555-9876
                Haley Snell (313)555-4938
        2. 数据变量
            更多的gawk内建变量
            变 量           描 述
            ARGC            当前命令行参数个数
            ARGIND          当前文件在 ARGV 中的位置
            ARGV            包含命令行参数的数组
            CONVFMT         数字的转换格式(参见 printf 语句),默认值为 %.6 g
            ENVIRON         当前shell环境变量及其值组成的关联数组
            ERRNO           当读取或关闭输入文件发生错误时的系统错误号
            FILENAME        用作gawk输入数据的数据文件的文件名
            FNR             当前数据文件中的数据行数
            IGNORECASE      设成非零值时,忽略 gawk 命令中出现的字符串的字符大小写
            NF              数据文件中的字段总数
            NR              已处理的输入记录数
            OFMT            数字的输出格式,默认值为 %.6 g
            RLENGTH         由 match 函数所匹配的子字符串的长度
            RSTART          由 match 函数所匹配的子字符串的起始位置
            ARGC与ARGV
                $ gawk 'BEGIN{print ARGC,ARGV[1]}' data1  -- ARGC 变量表明命令行上有两个参数。 ARGV 数组从索引 0 开始。
            ENVIRON 变量,使用关联数组来提取shell环境变量
                $ gawk '
                > BEGIN{
                > print ENVIRON["HOME"]
                > print ENVIRON["PATH"]
                > }'
                /home/rich
                /usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin
             NF 变量可以让你在不知道具体位置的情况下指定记录中的最后一个数据字段
                $ gawk 'BEGIN{FS=":"; OFS=":"} {print $1,$NF}' /etc/passwd    -- 
                rich:/bin/bash
                testy:/bin/csh
                mark:/bin/bash
                dan:/bin/bash
                mike:/bin/bash
                test:/bin/bash
            FNR 和 NR 变量虽然类似,但又略有不同。
                $ gawk '
                > BEGIN {FS=","}
                > {print $1,"FNR="FNR,"NR="NR}
                > END{print "There were",NR,"records processed"}' data1 data1  
                data11 FNR=1 NR=1
                data21 FNR=2 NR=2
                data31 FNR=3 NR=3
                data11 FNR=1 NR=4    -- FNR 变量的值在 gawk 处理第二个数据文件时被重置了,而 NR 变量则在处理第二个数据文件时继续计数。
                data21 FNR=2 NR=5
                data31 FNR=3 NR=6
                There were 6 records processed
                2 data1
    二、自定义变量
        gawk自定义变量名可以是任意数目的字母、数字和下划线,但不能以数字开头。注意: gawk 变量名区分大小写!
        1. 在脚本中给变量赋值(值可以是数字、文本,赋值支持数学算式)
            $ gawk '
            > BEGIN{
            > testing="This is a test"
            > print testing
            > testing=45
            > print testing
            > }'
            This is a test
            45    
            $ gawk 'BEGIN{x=4; x= x * 2 + 3; print x}'
            11    
        2. 在命令行上给变量赋值
            $ cat script1
            BEGIN{FS=","}
            {print $n}
            $ gawk -f script1 n=2 data1    -- 显示了文件的第二个数据字段
            data12
            data22
            data32
            用 -v 命令行参数,它允许你在 BEGIN 代码之前设定变量,否则设定的变量值在begin部分不可用。-v 命令行参数必须放在脚本代码之前。
            $ gawk -v n=3 -f script2 data1
            The starting value is 3
            data13
            data23
            data33            
#处理数组
    定义数组变量
        格式:var[index] = element  其中 var 是变量名,index 是关联数组的索引值,element 是数据元素值
            $ gawk 'BEGIN{
            > capital["Illinois"] = "Springfield"
            > print capital["Illinois"]
            > }'
            Springfield
            $
            $ gawk 'BEGIN{
            > var[1] = 34
            > var[2] = 3
            > total = var[1] + var[2]
            > print total
            > }'
            37        
    遍历数组变量
        gwak遍历格式:
            for (var in array)
            {
                statements
            }
        变量中存储的是索引值而不是数组元素值
            $ gawk 'BEGIN{
            > var["a"] = 1
            > var["g"] = 2
            > var["m"] = 3
            > var["u"] = 4
            > for (test in var)
            > {
            > print "Index:",test," - Value:",var[test]
            > }
            > }'
            Index: u - Value: 4
            Index: m - Value: 3
            Index: a - Value: 1
            Index: g - Value: 2            
    删除数组变量
        格式:delete array[index]
        删除命令会从数组中删除关联索引值和相关的数据元素值
            $ gawk 'BEGIN{
            > var["a"] = 1
            > var["g"] = 2
            > for (test in var)
            > {
            > print "Index:",test," - Value:",var[test]
            > }
            > delete var["g"]
            > print "---"
            > for (test in var)
            > print "Index:",test," - Value:",var[test]
            > }'
            Index: a - Value: 1
            Index: g - Value: 2
            ---
            Index: a - Value: 1
#使用模式
    正则表达式
        正则表达式必须出现在它要控制的程序脚本的左花括号前
            $ gawk 'BEGIN{FS=","} /11/{print $1}' data1
            data11
    匹配操作符
        匹配操作符是波浪线( ~ ),允许将正则表达式限定在记录中的特定数据字段
            $ gawk –F: '$1 !~ /rich/{print $1,$NF}' /etc/passwd    -- gawk程序脚本会打印/etc/passwd文件中与用户ID  rich 不匹配的用户ID和登录shell
            root /bin/bash
            daemon /bin/sh
            bin /bin/sh
            sys /bin/sh
            $ gawk -F: '$1 ~ /rich/{print $1,$NF}' /etc/passwd
            rich /bin/bash
            $ gawk 'BEGIN{FS=","} $2 ~ /^data2/{print $0}' data1
            data21,data22,data23,data24,data25
    数学表达式
        可以使用任何常见的数学比较表达式。
            x == y :值x等于y。
            x <= y :值x小于等于y。
            x < y :值x小于y。
            x >= y :值x大于等于y。
            x > y :值x大于y。
            $ gawk -F: '$4 == 0{print $1}' /etc/passwd    -- 显示所有属于root用户组(组ID为 0 )的系统用户
            root
            sync
            shutdown
            halt
            operator
        也可以对文本数据使用表达式,跟正则表达式不同,表达式必须完全匹配
            $ gawk -F, '$1 == "data"{print $1}' data1
            $
            $ gawk -F, '$1 == "data11"{print $1}' data1
            data11
#结构命令
    if语句
        gawk编程语言支持标准的 if-then-else 格式的 if 语句。你必须为 if 语句定义一个求值的条件,并将其用圆括号括起来
        格式:if (condition) statement1
            $ cat data4
            10
            5
            13
            50
            34
            $ gawk '{if ($1 > 20) print $1}' data4
            50
            34
            $ gawk '{
            > if ($1 > 20)
            > {    -- 执行多条语句,就必须用花括号将它们括起来
            > x = $1 * 2
            > print x
            > }
            > }' data4
            100
            68
        gawk 的 if 语句也支持 else 子句,允许在 if 语句条件不成立的情况下执行一条或多条语句
            $ gawk '{
            > if ($1 > 20)
            > {
            > x = $1 * 2
            > print x
            > } else
            > {
            > x = $1 / 2
            > print x
            > }}' data4
            5
            2.5
            6.5
            100
            68
    while语句
        while 循环允许遍历一组数据,并检查迭代的结束条件
        格式:
            while (condition)
            {
                statements
            }
            $ cat data5
            130 120 135
            160 113 140
            145 170 215
            $ gawk '{
            > total = 0
            > i = 1
            > while (i < 4)
            > {
            > total += $i
            > i++
            > }
            > avg = total / 3
            > print "Average:",avg
            > }' data5
            Average: 128.333
            Average: 137.667
            Average: 176.667
        gawk编程语言支持在 while 循环中使用 break 语句和 continue 语句,允许你从循环中跳出
            $ gawk '{
            > total = 0
            > i = 1
            > while (i < 4)
            > {
            > total += $i
            > if (i == 2)
            > break
            > i++
            > }
            > avg = total / 2
            > print "The average of the first two data elements is:",avg
            > }' data5
            The average of the first two data elements is: 125
            The average of the first two data elements is: 136.5
            The average of the first two data elements is: 157.5
    do—while语句
        do-while 语句类似于 while 语句,但会在检查条件语句之前执行命令
        格式:
            do
            {
                statements
            } while (condition)
            $ gawk '{
            > total = 0
            > i = 1
            > do
            > {
            > total += $i
            > i++
            > } while (total < 150)
            > print total }' data5
            250
            160
            315
    for语句
        gawk编程语言支持C风格的 for 循环
            格式:for( variable assignment; condition; iteration process)
                $ gawk '{
                > total = 0
                > for (i = 1; i < 4; i++)
                > {
                > total += $i
                > }
                > avg = total / 3
                > print "Average:",avg
                > }' data5
                Average: 128.333
                Average: 137.667
                Average: 176.667
#格式化打印
    格式:printf "format string", var1, var2 . . .    
    format string 是格式化输出的关键它会用文本元素和格式化指定符来具体指定如何呈现格式化输出。格式化指定符是一种特殊的代码,会指明显示什么类型的变量以及如何显示。gawk程序会将每个格式化指定符作为占位符,供命令中的变量使用。第一个格式化指定符对应列出的第一个变量,第二个对应第二个变量,依此类推
    格式化指定符格式:%[modifier]control-letter  -- 其中 control-letter 是一个单字符代码,用于指明显示什么类型的数据
        格式化指定符的控制字母
        控制字母  描 述
        c         将一个数作为ASCII字符显示
        d         显示一个整数值
        i         显示一个整数值(跟d一样)
        e         用科学计数法显示一个数
        f         显示一个浮点值
        g         用科学计数法或浮点数显示(选择较短的格式)
        o         显示一个八进制值
        s         显示一个文本字符串
        x         显示一个十六进制值
        X         显示一个十六进制值,但用大写字母A~F    
        科学计数法显示一个数
            $ gawk 'BEGIN{
            > x = 10 * 100
            > printf "The answer is: %e\n", x
            > }'
            The answer is: 1.000000e+03
        除了控制字母外,还有3种修饰符可以用来进一步控制输出:
            width :指定了输出字段最小宽度的数字值。如果输出短于这个值, printf 会将文本右对齐,并用空格进行填充。如果输出比指定的宽度还要长,则按照实际的长度输出。
            prec :这是一个数字值,指定了浮点数中小数点后面位数,或者文本字符串中显示的最大字符数。
            - (减号):指明在向格式化空间中放入数据时采用左对齐而不是右对齐。在使用 printf 语句时,你可以完全控制输出样式
                通过添加一个值为 16 的修饰符,我们强制第一个字符串的输出宽度为16个字符。默认情况下,printf 命令使用右对齐来将数据放到格式化空间中。要改成左对齐,只需给修饰符加一个减号即可            
                    $ gawk 'BEGIN{FS="\n"; RS=""} {printf "%-16s %s\n", $1, $4}' data2
                    Riley Mullen (312)555-1234
                    Frank Williams (317)555-9876
                    Haley Snell (313)555-4938
#内建函数
    数学函数
        gawk数学函数
            函 数        描 述
            atan2(x, y)  x/y的反正切,x和y以弧度为单位
            cos(x)       x的余弦,x以弧度为单位
            exp(x)       x的指数函数
            int(x)       x的整数部分,取靠近零一侧的值
            log(x)       x的自然对数
            rand( )      比0大比1小的随机浮点值
            sin(x)       x的正弦,x以弧度为单位
            sqrt(x)      x的平方根
            srand(x)    为计算随机数指定一个种子值
        gawk还支持一些按位操作数据的函数。
            and(v1, v2) :执行值 v1 和 v2 的按位与运算。
            compl(val) :执行 val 的补运算。
            lshift(val, count) :将值 val 左移 count 位。
            or(v1, v2) :执行值 v1 和 v2 的按位或运算。
            rshift(val, count) :将值 val 右移 count 位。
            xor(v1, v2) :执行值 v1 和 v2 的按位异或运算。
    字符串函数
        gawk字符串函数
        函 数                      描 述
        asort(s [,d])              将数组s按数据元素值排序。索引值会被替换成表示新的排序顺序的连续数字。另外,如果指定了d,则排序后的数组会存储在数组d中
        asorti(s [,d])             将数组s按索引值排序。生成的数组会将索引值作为数据元素值,用连续数字索引来表明排序顺序。另外如果指定了d,排序后的数组会存储在数组d中
        gensub(r, s, h [, t])      查找变量$0或目标字符串t(如果提供了的话)来匹配正则表达式r。如果h是一个以g或G开头的字符串,就用s替换掉匹配的文本。如果h是一个数字,它表示要替换掉第h处r匹配的地方
        gsub(r, s [,t])            查找变量$0或目标字符串t(如果提供了的话)来匹配正则表达式r。如果找到了,就全部替换成字符串s
        index(s, t)                返回字符串t在字符串s中的索引值,如果没找到的话返回 0
        length([s])                返回字符串s的长度;如果没有指定的话,返回$0的长度
        match(s, r [,a])           返回字符串s中正则表达式r出现位置的索引。如果指定了数组a,它会存储s中匹配正则表达式的那部分
        split(s, a [,r])           将s用 FS 字符或正则表达式r(如果指定了的话)分开放到数组a中。返回字段的总数
        sprintf(format,variables)  用提供的format和variables返回一个类似于printf输出的字符串
        sub(r, s [,t])             在变量$0或目标字符串t中查找正则表达式r的匹配。如果找到了,就用字符串s替换掉第一处匹配
        substr(s, i [,n])          返回s中从索引值i开始的n个字符组成的子字符串。如果未提供n,则返回s剩下的部分
        tolower(s)                 将s中的所有字符转换成小写
        toupper(s)                 将s中的所有字符转换成大写
        转换大写,返回长度
            $ gawk 'BEGIN{x = "testing"; print toupper(x); print length(x) }'
            TESTING
            7        
        排序
            $ gawk 'BEGIN{
            > var["a"] = 1
            > var["g"] = 2
            > var["m"] = 3
            > var["u"] = 4
            > asort(var, test)
            > for (i in test)
            > print "Index:",i," - value:",test[i]
            > }'
            Index: 4 - value: 4
            Index: 1 - value: 1
            Index: 2 - value: 2
            Index: 3 - value: 3        
    时间函数
        gawk的时间函数
        函 数                           描 述
        mktime(datespec)                将一个按YYYY MM DD HH MM SS [DST]格式指定的日期转换成时间戳值 ①
        strftime(format[,timestamp])    将当前时间的时间戳或timestamp(如果提供了的话)转化格式化日期(采用shell函数date()的格式)              
        systime( )                      返回当前时间的时间戳
        例如:
            $ gawk 'BEGIN{
            > date = systime()
            > day = strftime("%A, %B %d, %Y", date)
            > print day
            > }'
            Friday, December 26, 2014        
#自定义函数
    定义函数
        格式:
            function name([variables])
            {
                statements
            }
    使用自定义函数
        在定义函数时,它必须出现在所有代码块之前(包括 BEGIN 代码块),有助于将函数代码与gawk程序的其他部分分开
            $ gawk '
            > function myprint()
            > {
            > printf "%-16s - %s\n", $1, $4
            > }
            > BEGIN{FS="\n"; RS=""}
            > {
            > myprint()
            > }' data2
            Riley Mullen - (312)555-1234
            Frank Williams - (317)555-9876
            Haley Snell - (313)555-4938
    创建函数库
        $ cat funclib
        function myprint()
        {
            printf "%-16s - %s\n", $1, $4
        }
        function myrand(limit)
        {
            return int(limit * rand())
        }
        function printthird()
        {
            print $3
        }
        $ cat script4
        BEGIN{ FS="\n"; RS=""}
        {
            myprint()
        }
        $ gawk -f funclib -f script4 data2
        Riley Mullen - (312)555-1234
        Frank Williams - (317)555-9876
        Haley Snell - (313)555-4938
#实例
    计算保龄球锦标赛成绩
        $ cat scores.txt
        Rich Blum,team1,100,115,95
        Barbara Blum,team1,110,115,100
        Christine Bresnahan,team2,120,115,118
        Tim Bresnahan,team2,125,112,116
        $ cat bowling.sh
        for team in $(gawk –F, '{print $2}' scores.txt | uniq)
        do
            gawk –v team=$team 'BEGIN{FS=","; total=0}
        {
        if ($2==team)
        {
            total += $3 + $4 + $5;
        }
        }
        END {
            avg = total / 6;
            print "Total for", team, "is", total, ",the average is",avg
        }' scores.txt
        done
        $ sh bowling.sh
        Total for team1 is 635, the average is 105.833
        Total for team2 is 706, the average is 117.667

 

posted on 2019-07-17 10:30  丿星痕☆森文  阅读(246)  评论(0编辑  收藏  举报

导航