zoukankan      html  css  js  c++  java
  • shell基础 -- grep、sed、awk命令简介

      在 shell 编程中,常需要处理文本,这里介绍几个文本处理命令。

    一、grep 命令

    grep 命令由来已久,用 grep 命令来查找 文本十分方便。在 POSIX 系统上,grep 可以在两种正则表达式风格中选择一种(BRE 和 ERE),或是执行简单的字符串匹配。传统上,有三种程序可以用来查找整个文本文件:

    1)grep:最早的文本匹配程序。使用 POSIX 标准定义的基本正则表达(Basic Regular Expression,BRE);

    2)egrep:扩展 grep。使用扩展正则表达式(Extended Regular Expression,ERE);

    3)fgrep:快速 grep。匹配固定字符串而非正则表达式,它使用优化的算法,能更有效地匹配固定字符串。

      在目前的 POSIX 标准中,这三个程序已经被整合成为一个程序 grep,通过对 grep 命令加以不同的选项进行选择控制。

      grep 命令由一个选项、一个要匹配的模式和要搜索的文件组成,语法如下: 

    grep [options] PATTERN [FILES]

      如果没有提供文件名,则 grep 命令将搜索标准输入。grep 命令将会根据所提供的模式对文件进行匹配,发现匹配查找模式的行时,将该行显示出来。当 grep 命令同时搜索多个文件时,将会在搜索结果每一行前面加上文件名与一个冒号。grep 命令的主要选项如下:

    选项 功能
    -E 使用扩展正则表达式进行匹配(取代传统的 egrep 命令)
    -F 使用固定字符串进行匹配(取代传统的 fgrep 命令)
    -c   输出匹配行的数目,而不是输出匹配的行
    -h 取消每个输出行的普通前缀,即匹配查询模式的文件名
    -i 忽略大小写
    -l 只列出包含匹配行的文件名,而不输出真正的匹配行
    -v 对匹配模式取反,即搜索匹配不到的行
    -n 输出行号

       用例子演示一下:

    [tongye@localhost ~]$ grep -ni ROOT /etc/passwd
    1:root:x:0:0:root:/root:/bin/bash
    10:operator:x:11:0:operator:/root:/sbin/nologin

      该命令将会在 /etc/passwd 中查找有 root 的行,并将该行显示出来, -n 选项输出行号,-i 选项忽略大小写。

    二、sed 命令

      sed( stream editor 流编辑器) ,可以用来在管道或者命令序列中编辑数据。sed 的语法如下:

    sed option command file

      其中,command 是命令部分,用来指示 sed 该执行何种操作,file 则是 sed 命令将要操作的对象,通常是一个文件,如果没有文件,则使用标准输入。option 是 sed 命令可以使用的选项,主要有三个选项: -n、-e、-f,在后面再介绍。

      sed 命令读取每一个文件,一次读一行,将读取的行放到内存的一个区域--称为模式空间(pattern space),所有编辑上的操作都会应用到模式空间的内容。当所有操作完成后,sed 命令会将模式空间的最后内容打印到标准输出,再回到开始处,读取另一个输入行。为了演示 sed 命令,笔者写了一小段文本 test.txt 用作试验的素材(英语差,语法问题请忽略)

    hello,my name is tongye
    I want to write a program named HelloWorld.c
    now,let`s begin
    #include "stdio.h"
    main(){
            printf("Hello world");
    }
    oh,it`s symple
    writed by tongye
    end

    2.1  使用 s 参数执行替换操作

      sed 命令一个常用的功能是进行替换操作,sed 替换操作的一般格式如下:

    sed 's/string1/string2/' file    # 将文件中每行的第一个 string1 替换成 string2

      在上述语句中,参数 s 表示这是一个替换操作,/ 字符是界定符,用于分隔正则表达式与替代文本。界定符可以是任何可显示的字符,但是 / 字符是最常用的界定符。另外,在处理文件名称时,一般使用分号、冒号或逗号作为界定符。string1 是被替换的文本,可以是正则表达式;string2 是替换文本。

    [tongye@localhost Shell_Program]$ sed 's/tongye/ttyezi/' test.txt 
    hello,my name is ttyezi
    I want to write a program named HelloWorld.c
    now,let`s begin
    #include "stdio.h"
    main(){
            printf("Hello world");
    }
    oh,it`s symple
    writed by ttyezi
    end

       需要注意的是,上述语句只能替换第一个匹配到的文本,若想要将每一个匹配到的文本都替换掉,需要在结尾加上 g 参数(global),即:

    sed 's/string1/string2/g' file    # 将文件中所有的 string1 都替换成 string2

      如果需要删除文本中的一个字符串,可以在替换文本处不放入任何文本(即空)来实现,如下:

    sed 's/string1//g' file               # 删除文件中所有的 string1

    2.2 使用 -e 选项和 -f 选项同时执行多个编辑命令

      当 sed 后面需要同时接多个编辑命令的时候,需要使用 -e 选项。每一个编辑命令都使用一个 -e 选项,如:

    [tongye@localhost Shell_Program]$ sed -e 's/tongye/ttyezi/g' -e 's/HelloWorld/helloworld/g' test.txt
    hello,my name is ttyezi
    I want to write a program named helloworld.c
    now,let`s begin
    #include "stdio.h"
    main(){
            printf("Hello world");
    }
    oh,it`s symple
    writed by ttyezi
    end

      当需要编辑的项目很多时,如果把每一个编辑命令都接到 sed 后面,无疑会让代码很复杂,不易阅读且容易出错。这时,可以将所有的编辑命令都写进一个脚本,再使用 sed 搭配 -f 选项来操作:

    # substitute.sed 存放着编辑命令 
    s/tongye/ttyezi/g
    s/HelloWorld/helloworld/g
    s;^(.).*1$;The first letter of this line is the same as its last letter;
    
    
    [tongye@localhost Shell_Program]$ sed -f substitute.sed test.txt                                
    hello,my name is ttyezi
    I want to write a program named helloworld.c
    The first letter of this line is the same as its last letter
    #include "stdio.h"
    main(){
            printf("Hello world");
    }
    oh,it`s symple
    writed by ttyezi
    end
    View Code

    2.3 使用 -n 选项与 p 参数打印特定的行

      sed 默认情况下会把输入的每一行都打印到标准输出上,如果不想输出所有行,可以使用 -n 选项。使用了 -n 选项的 sed 命令将不打印任何行, -n 选项通常与参数 p 配合起来使用,p 参数可以让 sed 命令打印出符合指定范围或模式的所有行:

    [tongye@localhost Shell_Program]$ sed -n 's/tongye/ttyezi/p' test.txt       
    hello,my name is ttyezi
    writed by ttyezi

      该语句将文件中的 tongye 替换成 ttyezi,并且只打印发生替换操作的两行;

    [tongye@localhost Shell_Program]$ sed -n '1,3p' test.txt        # 打印 1 到 3 行      
    hello,my name is tongye
    I want to write a program named HelloWorld.c
    now,let`s begin
    
    [tongye@localhost Shell_Program]$ sed -n '4p' test.txt       # 只打印第 4 行
    #include "stdio.h"

      使用 sed -n '1,3p' 来只打印文件的前三行,注意这里的 '1,3' 表示的是一个范围;

    [tongye@localhost Shell_Program]$ sed -n '/HelloWorld/p' test.txt 
    I want to write a program named HelloWorld.c

      该命令只打印包含 HelloWorld 的行。

    2.4 使用 d 参数执行删除操作

      要删除某一个特定的行,可以使用参数 d,只需要指定行号或者行范围,就可以从输入中删除指定的行:

    [tongye@localhost Shell_Program]$ sed '1,4d' test.txt      # 删除第 1 到 4 行,然后将剩余的行打印到标准输出
    main(){
            printf("Hello world");
    }
    oh,it`s symple
    writed by tongye
    end

       sed 也可以使用参数 d 来删除符合匹配模式的行:

    [tongye@localhost Shell_Program]$ sed '/tongye/d' test.txt     # 删除所有含有 tongye 的行
    I want to write a program named HelloWorld.c
    now,let`s begin
    #include "stdio.h"
    main(){
            printf("Hello world");
    }
    oh,it`s symple
    end

    2.5 使用 -i 选项或输出重定向来保存 sed 编辑的内容

      在上面的所有操作中,我们发现虽然 sed 操作确实完成了,输出到标准输出上的文本内容确实发生了变化,但是如果我们再次打开所编辑的文件,会发现文件内容并没有发生更改。如果需要保存 sed 编辑的内容,可以使用 -i 选项:

    [tongye@localhost Shell_Program]$ sed -i 's/tongye/ttyezi/' test.txt 
    [tongye@localhost Shell_Program]$ cat test.txt                        
    hello,my name is ttyezi
    I want to write a program named HelloWorld.c
    now,let`s begin
    #include "stdio.h"
    main(){
            printf("Hello world");
    }
    oh,it`s symple
    writed by ttyezi
    end
    View Code

      也可以使用输出重定向的方式,将编辑后的内容保存到一个新的文本文件中而不是输出到标准输出:

    [tongye@localhost Shell_Program]$ sed 's/hello/HELLO/' test.txt > test1.txt 
    [tongye@localhost Shell_Program]$ cat test1.txt 
    HELLO,my name is ttyezi
    I want to write a program named HelloWorld.c
    now,let`s begin
    #include "stdio.h"
    main(){
            printf("Hello world");
    }
    oh,it`s symple
    writed by ttyezi
    end
    View Code

    三、awk 命令

    3.1 awk 的基本模式与操作 

      shell 中提供 awk 命令来重新编排字段。实际上, awk 本身所提供的功能十分完备,已经是一种很好用的程序语言了,这里暂且只讨论它在 shell 脚本中的一些长处:文本处理功能。awk 命令的基本模式如下:

    awk 'program' [ file ...]

      awk 读取命令行上所指定的各个文件(若没有文件,则为标准输入),一次读取一条记录(行),再针对每一行,应用 program 所指定的命令。 awk程序(program)基本架构为:

    pattern {action}
    pattern {action}
    ...

      pattern 部分几乎可以是任何表达式,但是在单命令行程序里,它通常是由斜杠括起来的 ERE。 action 为任意的 awk 语句,但是在单命令行程序里,通常是一个直接明了的 print 语句。pattern 或 action 都可以省略(不要全部省略)。省略 pattern,则会对每一条输入记录执行 action;省略 action 则默认 action 为{ print } ,将打印显示整条记录。

    [tongye@localhost etc]$ awk '/root/ {print}' /etc/passwd
    root:x:0:0:root:/root:/bin/bash
    operator:x:11:0:operator:/root:/sbin/nologin

      上述指令将打印文本中所有包含 root 的行。   

    3.2 字段

      awk 设计的重点就在字段与记录上:awk 读取输入记录(通常是一些行),然后自动将各个记录切分为字段。awk 将每条记录内的字段数目,存储到内建变量 NF 中。awk 默认以空白分隔字段(如空格符、制表符),不过,也可以设置成其他的,通过设置 FS 变量(列数据分隔符)。如果需要使用字段值,可以使用 $ 字符来引用,$1表示第一个字段值、$2表示第二个字段值、...  另外,$0表示整条记录。举个例子验证一下:

    [tongye@localhost Shell_Program]$ awk '{print $1}' test.txt 
    hello,my
    I
    now,let`s
    #include
    main(){
    printf("Hello
    }
    oh,it`s
    writed
    end

      这句指令将会按照默认的分隔符(空白符)来对字段进行划分,并打印第一个字段的值到标准输出。

    3.3 设置字段分隔符

      可以使用 -F 选项来修改字段分隔符。-F 选项会自动的设置 FS 变量,只需将 FS 变量放到 -F 选项后面即可。FS 变量可以被设置为单个字符(此时,只要该字符出现一次,就分隔出一个字段),也可以被设置为一个完整的 ERE(此时,每一个匹配该 ERE 的文本都将被视为字段分隔符):

    awk -F: '{print $1,$2}' /etc/passwd

      这段指令将使用冒号 : 作为字段分隔符去处理 /etc/passwd 文件,然后输出文件的第1、第2个字段到标准输出。

      -F 选项设置的字段分隔符是相对于输入而言的。而 awk 的输入、输出分隔符用法是分开的,因此即使使用 -F 选项设置了 FS 变量,输出的字段分隔符还是默认的空白符,这样可能会影响结果判断,使用 -v 选项可以设置输出字段分隔符,通过改变 OFS 变量(列数据输出分隔符)的值,使用形式也 -F 选项有所区别:

    awk -v 'OFS=/' '{print $1,$2}' /etc/passwd

      -v 选项的用法如上,该指令的将把 awk 的输出与分隔符设置为斜杠符 / 。举一个例子:

    [tongye@localhost Shell_Program]$ awk -F '/..' -v 'OFS=/' '{ print $1,$2,$3 }' /etc/passwd
    root:x:0:0:root:/ot:/n
    bin:x:1:1:bin:/n:/in
    daemon:x:2:2:daemon:/in:/in
    adm:x:3:4:adm:/r/m:
    lp:x:4:7:lp:/r/ool
    sync:x:5:0:sync:/in:/n
    shutdown:x:6:0:shutdown:/in:/in
    halt:x:7:0:halt:/in:/in
    mail:x:8:12:mail:/r/ool
    operator:x:11:0:operator:/ot:/in
    games:x:12:100:games:/r/mes:
    ftp:x:14:50:FTP User:/r/p:
    nobody:x:99:99:Nobody:/sbin/login
    systemd-network:x:192:192:systemd Network Management:/sbin/login
    dbus:x:81:81:System message bus:/sbin/login
    polkitd:x:999:998:User for polkitd:/sbin/login
    tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/v/ll:
    abrt:x:173:173::/c/rt:
    sshd:x:74:74:Privilege-separated SSH:/r/pty
    postfix:x:89:89::/r/ool
    chrony:x:998:996::/r/b
    tongye:x:1000:1000:tongye:/me/ngye:

      这段指令使用正则表达式 /.. 作为段分隔符去处理 /etc/passwd 文件,文件中每个匹配该正则表达式的文本都被视为一个字段分隔符。然后使用斜杠符 / 作为输出字段分隔符,并将第1、2、3个字段输出到标准输出。

    3.4 打印行 print 与 printf

      print 上面已经用到过,这是 awk 里面最常使用的一条语句,可以用来进行简单的打印工作。print 的参数可以是字段列表、变量或者字符串:

    [tongye@localhost Shell_Program]$ awk -F: -v 'OFS=:' '{print "username is",$1}' /etc/passwd

      print 命令将后面的参数一个一个打印到标准输出,如果没有后接参数,则默认参数为 $0,将打印整条记录。注意,print 的参数之间需要用逗号隔开,否则输出结果将会连到一起没有间隔。

      对于上面的语句,print 后面的参数混合了字符串和变量,当参数较多时,写起来会比较不方便。此时,可以使用 printf 语句来替代 print 语句。printf 语句可以将所有参数放到一对双引号中去,与 C 中的 printf 用法类似:

    [tongye@localhost Shell_Program]$ awk -F: '{printf "username is %s
    ",$1}' /etc/passwd
    username is root
    username is bin
    username is daemon
    username is adm
    username is lp
    username is sync
    username is shutdown
    username is halt
    username is mail
    username is operator
    username is games
    username is ftp
    username is nobody
    username is systemd-network
    username is dbus
    username is polkitd
    username is tss
    username is abrt
    username is sshd
    username is postfix
    username is chrony
    username is tongye

      需要注意的是,awk 的 print 语句会自动提供换行符,而 printf 语句不能,需要自己提供 来进行换行。

    参考资料:

    《Linux 程序设计 第四版》

    《Shell 脚本学习指南》

    《UNIX/Linux/OS X 中的 Shell 编程 第四版》

  • 相关阅读:
    lintcode:最大子正方形
    lintcode 中等题:k Sum ii k数和 II
    lintcode 中等题:A + B Problem A + B 问题
    Protege汉字不能正常显示问题
    Protege A DOT error has occurred错误
    lintcode :reverse integer 颠倒整数
    Reported time is too far out of sync with master. Time difference of 52692ms > max allowed of 30000ms
    Please add or free up more resources then turn off safe mode manually.
    Permission denied: user=root, access=WRITE, inode="/":hadoopuser:supergroup:drwxr-xr-x
    Hadoop重新格式化HDFS的方法
  • 原文地址:https://www.cnblogs.com/tongye/p/9747560.html
Copyright © 2011-2022 走看看