• 彻底理解 Linux 的搜索工具: grep 和 awk


    grep 官方手册
    awk 官方手册awk 学习资料

    1. grep

    grep 用于打印匹配指定模式的行。

    1.1 介绍

    grep 命令从输入文件中查找匹配到给定模式列表的行。发现匹配到的行后,默认情况下会复制这一行到标准输出流,也可以通过选项产生任何其他类型的输出。

    grep 匹配文本时,对输入行长度没有限制(但受内存限制),并且可以匹配一行中的任意字符。如果输入文件的最后一个字节不是换行符,那么 grep 会提供一个。由于换行符也是模式列表的分隔符,因此无法在文本中匹配换行符。

    1.2 调用 grep

    grep [options] pattern input_file_names

    选项可以有零个或多个。如果通过选项 -e pattern-f file 指定了模式(pattern),命令中的模式是不可见的。input_file_names 也可以有零个或多个。

    1.2.1 命令行选项

    grep 带有一组丰富的选项,分别来自 POSIX 或 GNU 扩展。长选项都是 GNU 扩展的,即使对于 POSIX 规范中的选项也是如此。

    通用程序信息

    • --help:打印选项帮助信息并退出。
    • -V--version:将 grep 的版本号打印到标准输出流。

    匹配控制

    • -e pattern--regexp=pattern:从命令行指定模式。如果此选项多次使用或与 -f (--file) 选项结合使用,搜索所有给定的模式。 (-e 由 POSIX 指定。)
    • -f file--file=file:从文件中获取模式,每行一个。如果此选项多次使用或与 -e--regexp)选项结合使用,搜索所有给定的模式。 空文件包含零个模式,因此不匹配任何内容。(-f 由 POSIX 指定)。
    • -i-y--ignore-case:忽略大小写,以便匹配只是大小写不同的字符。
    • -v--invert-match:对匹配到的结果取反,选择不匹配的行。(-v 由 POSIX 指定。)
    • -w--word-regexp:只选择那些包含整个单词的匹配的行。测试是匹配子字符串必须位于行首,或者以非单词组成字符开头。同样,它必须位于行尾,或者后面跟着一个非单词组成字符。单词组成字符是字母,数字和下划线。如果还指定了 -x,则此选项无效。
    • -x--line-regexp:只选择与整个行完全匹配的那些行。对于一个正则表达式模式,这就像括号中的模式,然后用’^’和’$’围绕它。 (-x 由 POSIX 指定。)

    通用输出控制

    • -c``--count:抑制正常输出,相反,为每个输入文件打印一个匹配行数。使用 -v--invert-match)选项可以计算不匹配的行。
    • --color[=WHEN]``--colour[=WHEN]:将匹配(非空)字符串,匹配行,上下文行,文件名,行号,字节偏移量和分隔符(用于上下文行的字段和组)排列用转义序列包围后在终端上以彩色显示。对于粗体红色,颜色由环境变量 GREP_COLORS 定义,默认为’ms = 01; 31:mc = 01; 31:sl =:cx =:fn = 35:ln = 32:bn = 32:se = 36’ 粗体红色的匹配文本,品红色的文件名称,绿线的数字,绿色的字节偏移量,青色的分隔符和默认终端颜色。WHEN 可以是‘never’、‘always’ 或 ‘auto’。
    • -L``--files-without-match:抑制正常输出,相反,打印每个输入文件的名称,其中通常不会输出任何输出文件(from which no output would normally have been printed)。每个文件的扫描在第一次匹配时停止。
    • -l``--files-with-matches:抑制正常输出,相反,打印每个输出文件的名称,通常从哪个输出文件打印出来(from which output would normally have been printed)。 每个文件的扫描在第一场比赛时停止。
    • -m num``--max-count=num:匹配到指定次数后停止读取文件。如果输入是来自常规文件的标准输入,并且输出了 num 个匹配行,则 grep 可确保在退出前标准输入位于最后一个匹配行之后,而不管后面是否存在上下文行。这使调用进程能够恢复搜索。例如,以下 shell 脚本使用这个选项:
    while grep -m 1 PATTERN
    do
      echo xxxx
    done < FILE

    但以下可能无法正常工作,因为管道 pipe 不是常规文件:

    # This probably will not work.
    cat FILE |
    while grep -m 1 PATTERN
    do
      echo xxxx
    done

    当 grep 在 num 个匹配行后停止时,它输出任何后面的上下文行。由于上下文不包含匹配行,当遇到另一个匹配行时,grep 将停止。当还使用 -c--count 选项时,grep 不会输出大于 num 的计数。当使用 -v--invert-match 选项时,grep 在输出 num 不匹配行后停止。
    - -o``--only-matching:仅打印匹配行的匹配(非空)部分,每个这样的部分位于单独的输出行上。输出行使用与输入相同的分隔符,如果还使用了 -z--null-data),则分隔符为空字节(请参阅其他选项)。
    - -q``--quiet``--silent:不向标准输出写任何东西。如果发现任何匹配,即使检测到错误,也立即以零状态退出。另请参阅 -s--no-messages 选项。
    - -s``--no-messages:抑制关于不存在或不可读文件的错误消息。

    输出行的前缀控制(Output Line Prefix Control):

    当要输出多个前缀字段时,顺序始终是文件名,行号和字节偏移量,无法指定顺序。
    - -b``--byte-offset:在每行输出之前打印在输入文件中基于 0 的字节偏移量。如果指定了 -o--only-matching),则打印匹配部分本身的偏移量。当 grep 在 MS-DOS 或 MS-Windows 上运行时,打印的字节偏移取决于是否使用 -u--unix-byte-offsetsets)选项。
    - -H``--with-filename:每次匹配时打印文件名。同时搜索多个文件时默认开启。
    - -h``--no-filename:抑制输出前缀中的文件名。只搜索一个文件时默认开启。
    - --label=LABEL:显示实际来自标准输入的输入作为来自文件 LABEL 的输入。这在使用 zgrep 这样的工具时特别有用,例如:

    gzip -cd foo.gz | grep --label=foo -H something
    • -n``--line-number:在每个输出行的前面添加输入文件中基于 1 的行号。
    • -T``--initial-tab:确保实际行内容的第一个字符位于制表位 tab 上,以便对齐。对于将输出前缀(文件名、行号、字节偏移量)添加到实际内容的选项非常有用:-H-n-b。这也可能会预留空格来输出行号和字节偏移量,以使单个文件中的行全部从同一列开始。
    • -u``--unix-byte-offsets:只在 Windows 下有效。报告 Unix 风格的字节偏移量。该选项会导致 grep 报告字节偏移量,就像该文件是 Unix 样式的文本文件一样,即字节偏移量会忽略被删除的回车符。这将产生与在 Unix 机器上运行 grep 相同的结果。除非也使用 -b 选项,否则此选项不起作用。
    • -Z``--null:输出一个零字节(ASCII NUL 字符),而不是通常跟在文件名后面的字符。例如,’grep -lZ’在每个文件名后面输出一个零字节,而不是通常的换行符。该选项使输出清晰,即使在包含不正常字符(如换行符)的文件名中也是如此。这个选项可以和’find -print0’,’perl-0’,’sort -z’和’xargs -0’这样的命令一起使用来处理任意文件名,甚至那些包含换行符的文件名。

    上下文行控制(Context Line Control):

    上下文行是匹配行附近的非匹配行。只有在使用以下选项之一时才会输出它们。无论如何设置这些选项,grep 都不会多次输出任何给定的行。如果指定了 -o--only-matching)选项,则这些选项无效,并在使用时发出警告。
    - -A num``--after-context=num:打印匹配行后的上下文行中的 num 行。
    - -B num``--before-context=num:打印匹配行前的上下文行中的 num 行。
    - -C num``-num``--context=num:打印匹配行前和匹配行后的上下文行中的 num 行。
    - --group-separator=string:当使用 -A-B-C 选项时,在每组行之间打印字符串而不是 --
    - --no-group-separator:当使用 -A-B-C 选项时,每组行之间不再打印字符串。

    以下是关于 grep 如何选择分隔符以在前缀字段和行内容之间打印的一些要点:

    • 匹配行通常使用’:’作为前缀字段和实际行内容之间的分隔符。
    • 上下文(即非匹配行)行使用’-‘代替。
    • 当未指定上下文时,匹配行将被一个接一个地输出。
    • 指定上下文时,输入中相邻的行形成一个组,并且一个接一个地输出,而默认情况下,分隔符会出现在非相邻组之间。
    • 默认分隔符是’-‘行,它的存在和外观可以通过上述选项进行更改。
    • 每个组可能包含几个匹配行,当它们彼此足够靠近时,两个相邻的组连接并可合并成一个连续的组。

    文件及目录选择(File and Directory Selection):

    • -a``--text:像处理文本一样处理二进制文件,相当于’–binary-files=text’选项。
    • --binary-files=type:如果文件的数据或元数据指示文件包含二进制数据,则假定该文件是 type 类型的。非文本字节表示二进制数据,这些输出字节是针对当前语言环境进行错误编码的输出字节(请参阅环境变量 Environment Variables),或者当没有给出 -z--null-data)选项时输入空字符(请参阅其他选项 Other Options)。
      默认情况下,type 是’binary’,并且 grep 发现输入的二进制数据不存在时会抑制输出,并抑制包含不正确编码数据的输出行。当某些输出被抑制时,grep 会跟随带有单行消息的任何输出,表示二进制文件匹配。
      如果 type 是’without-match’,当 grep 发现输入的二进制数据不存在时,它假定文件的其余部分不匹配。相当于 -I 选项。
      如果 type 是’text’,grep 会像处理文本一样处理二进制数据。相当于 -a 选项。
      当 type 是’binary’时,即使没有 -z--null-data)选项,grep 也可能将非文本字节视为行终止符。这意味着选择“binary”与“text”可以影响模式是否与文件匹配。例如,当 type 为’binary’时,模式’q$’可能会匹配紧接着为空字节的’q’,尽管当 type 为’text’时这不匹配。相反,当 type 为’binary’时,模式’.’(句点)可能不匹配空字节。
      警告:-a--binary-files=text)选项可能会输出二进制垃圾,如果输出是终端,并且终端驱动程序将其中的一部分解释为命令,则可能会产生副作用。另一方面,当阅读编码未知的文本文件时,在环境中使用 -a 或设置 LC_ALL='C' 可能会有所帮助,以便找到更多匹配,即使匹配对于直接不安全显示。

    • -D action``--devices=action:如果输入文件是设备,FIFO 或套接字,则使用 action 来处理它。如果 action 是“read”,则所有设备都被读取,就像它们是普通文件一样。如果 action 是“skip”,则设备,FIFO 和套接字将被默认跳过。默认情况下,如果设备在命令行上或者使用了 -R--deference-recursive)选项,则读取设备,如果递归地遇到设备并使用 -r--recursive)选项,则会跳过设备。该选项对通过标准输入读取的文件没有影响。

    • -d action``--directories=action:如果输入文件是目录,则使用 action 来处理。默认情况下,action 是’read’,这意味着对目录的读取跟普通文件一样(一些操作系统和文件系统不允许这样做,并且会导致 grep 打印每个目录的错误消息或者静默跳过它们)。如果 action 是“skip”,则目录会被静默跳过。如果 action 是’recurse’,则 grep 会以递归方式读取每个目录下的所有文件,遵循命令行符号链接并跳过其他符号链接,相当于 -r 选项。
    • --exclude=glob:跳过任何名称后缀匹配到模式 glob 的命令行文件,使用通配符匹配。名称后缀可以是全名,或在 / 之后和非 / 之前的部分。当递归搜索时,跳过基本名称与 glob 匹配的任何子文件,基本名称是最后一个 / 之后的部分。模式可以使用 *?[...] 作为通配符,而 可以直接引用通配符或反斜线字符。
    • --exclude-from=file:跳过名称与 file 的模式相匹配的文件(使用通配符匹配,如 --exclude 下所述)。
    • --exclude-dir=glob:跳过任何名称后缀匹配模式 glob 的命令行目录。当递归搜索时,跳过其基名与 glob 匹配的任何子目录。忽略 glob 中的任何冗余结尾斜杠。
    • -I:处理二进制文件,就好像它不包含匹配数据一样; 这相当于 --binary-files=without-match 选项。
    • --include=glob:只搜索名称与 glob 匹配的文件,使用 --exclude 下所述的通配符匹配。
    • -r``--recursive:对于每个目录操作数,递归读取并处理该目录中的所有文件。遵循命令行上的符号链接,但跳过递归遇到的符号链接。注意,如果没有给出文件操作数,grep 将搜索工作目录。这与 --directories=recurse 选项相同。
    • -R``--dereference-recursive:对于每个目录操作数,按照所有符号链接递归地读取并处理该目录中的所有文件。

    Other Options:

    • --line-buffered:在输出上使用行缓冲。这可能会导致性能损失。
    • -U``--binary:将文件视为二进制文件。默认情况下,在 Windows 下,grep 根据 --binary-files 选项所描述的猜测文件是文本文件还是二进制文件。如果 grep 认为该文件是一个文本文件,它将从原始文件内容中删除回车符(以使表达式的 ^$ 正常工作)。指定 -U 推翻了这种猜测,导致所有文件被读取并逐字传递给匹配机制。如果文件是每行末尾有 CR/LF 对的文本文件,则会导致一些正则表达式失败。此选项对 Windows 以外的平台没有影响。
    • -z``--null-data:将输入和输出数据视为行序列,每个数据以零字节(ASCII 的 NUL 字符)而不是换行符结尾。像 -Z--null 选项一样,该选项可以用于像 sort -z 这样的命令来处理任意文件名。

    1.2.2 环境变量

    grep 的行为受环境变量的影响。

    GREP_OPTIONS:废弃。指定要放置在任何显式选项前面的默认选项。由于这会在编写可移植脚本时造成问题,所以在未来的 grep 发行版中将删除此功能。请改用别名或脚本。例如,如果 grep 位于目录 /usr/bin 中,则可以将 $HOME/bin 预先添加到 PATH 中,并创建包含以下内容的可执行脚本 $HOME/bin/grep

    #! /bin/sh
    export PATH=/usr/bin
    exec grep --color=auto --devices=skip "$@"

    GREP_COLOR:指定用于突出显示匹配(非空)文本的颜色。不赞成但仍支持使用 GREP_COLORS。GREP_COLORS 的’mt’,’ms’和’mc’功能优先于它。它只能指定用于突出显示任何匹配行中匹配的非空文本的颜色(当 -v 命令行选项被省略时的选定行,或者指定 -v 时的上下文行)。 默认值为’01;31’,表示终端默认背景上的粗体红色前景文字。

    更多环境变量,参考 这里

    1.2.3 退出状态

    正常情况下,如果选择了一行,退出状态为 0,如果未选择行,则退出状态为 2,如果发生错误,退出状态为 2。但是,如果使用 -q--quiet--silent 选项并选择一行,即使发生错误,退出状态也为 0。其他 grep 实现可能会以大于 2 的状态退出。

    1.3 正则表达式

    参考 这里

    1.4 使用

    配合管道

    基于上一个命令的输出信息进行 grep 匹配,而不是基于文件:

    # echo "hello world" | grep "hello"
    hello world

    在文件中查找匹配模式的行

    假设文件内容为:

    123
    hello
    hello world
    hello
    hello world
    666
    fine
    grep "hello world" /home/user/1.txt     # 匹配到一行
    grep [0-9] /home/user/1.txt              # 匹配到两行

    在多个文件中查找

    grep "match_pattern" file_1 file_2 file_3 ...

    输出包含匹配字符串的行数 -n,从 1 开始:

    # grep -n "hello world" 1.txt 
    3:hello world
    5:hello world

    统计包含匹配字符串的总行数:

    # grep -c "hello world" 1.txt 
    2

    在多个文件中搜索并输出匹配到的文件:

    grep -l "text" file1 file2 file3...

    在多级目录中对文本进行递归搜索:

    grep "text" . -r -n

    忽略匹配样式中的字符大小写:

    echo "hello world" | grep -i "HELLO"
    hello

    静默输出:

    不会输出任何信息,如果命令运行成功返回 0,失败则返回非 0 值。一般用于条件测试。

    grep -q "test" 1.txt

    2. awk

    awk 是一种程序设计语言,语言风格类似 C 语言,设计目的是写那种一行搞定事情的脚本,常用于文本处理的脚本。包含常用的内置函数,支持用户函数和动态正则表达式,支持数组。

    awk 是一种弱类型语言,不需要提前声明就可以使用变量,变量的类型转换也是隐含的,在不同的上下文中变量可能是不同类型。awk 的字符串连结操作不需要任何操作符,只要把需要连结的串并列写在一起即可。

    2.1 语法及命令

    1. awk 命令调用语法

    awk [options] 'script' var=value file(s)
    awk -v var=value [options] 'script' file(s)
    awk [options] -f scriptfile var=value file(s)

    2. awk 常用选项

    • -F fs:指定输入分隔符,fs 可以是字符串或正则表达式,如 -F:
    • -v var=value:自定义用户变量,将外部变量传递给 awk
    • -f scripfile:从脚本文件中读取 awk 命令

    3. awk 脚本结构

    awk 'BEGIN{ print "start" } { commands } END{ print "end" }' file

    awk 脚本通常由:BEGIN 语句块、能够使用模式匹配的通用语句块、END 语句块 3 部分组成,这三个部分都是可选的。脚本通常是被单引号或双引号中,所有指令用分号分隔。示例:

    awk 'BEGIN{ i=0 } { i++ } END{ print i }' filename
    awk "BEGIN{ i=0 } { i++ } END{ print i }" filename

    4. print 及 printf 指令

    awk 中的 print 指令不带参数时打印当前行,参数可以是逗号分隔的列表。awk 的 print 中双引号被当作字符串连接符使用,例如:

    # echo | awk '{a="a"; b="b"; print a,b}'
    a b
    # echo | awk '{a="a"; b="b"; print a","b}'
    a,b

    printf 指令跟 C 语言使用一样的格式化字符,以”%”开始,后跟一个或几个规定字符,用来确定输出内容格式。

    格式 描述
    %d 十进制有符号整数
    %u 十进制无符号整数
    %f 浮点数
    %s 字符串
    %c 单个字符
    %p 指针的值
    %e 指数形式的浮点数
    %x %X 无符号以十六进制表示的整数
    %o 无符号以八进制表示的整数
    %g 自动选择合适的表示法
    [root@VM_157_18_centos ~]# awk 'BEGIN{s="hello";f=124.113;i=246;c="hello"; printf("%s, %u, %.2f, %.2g, %X, %o, %c", s, f, f, f, i, i, c);}'
    hello, 124, 124.11, 1.2e+02, F6, 366, h

    2.2 awk 变量

    1. 内置变量

    • $n:当前记录的第 n 个字段,空白符分隔一行为多个字段。
    • $0:执行过程中当前行的文本内容,完整的行。
    • NR:Number of Record,从 1 开始计数。awk 开始执行后,按照记录分隔符读取数据的次数(默认的记录分隔符为换行符,因此就是读取行数)。多个输入文件时,处理完第一个文件后,NR 继续累加而不会清空。
    • FNR:File Number of Record,每当处理一个新文件的时候,FNR 就从 1 开始计数。
    • NF:Number of Field,当前记录中字段的个数。
    • ARGC:命令行参数的个数。
    • ARGV:包含命令行参数的数组。
    • ARGIND:命令行中当前文件的位置(从 0 开始算)。
    • CONVFMT:数字转换格式(默认值为 %.6g)。
    • ENVIRON:环境变量关联数组。
    • ERRNO:最后一个系统错误的描述。
    • FIELDWIDTHS:字段宽度列表(用空格键分隔)。
    • FILENAME:当前输入文件的名。
    • FS:Field Separator,字段分隔符(默认是任何空白字符)。
    • IGNORECASE:如果为真,则进行忽略大小写的匹配。
    • OFMT:数字的输出格式(默认值是 %.6g)。
    • OFS:输出字段分隔符(默认值是一个空格)。
    • ORS:输出记录分隔符(默认值是一个换行符)。
    • RS:Record Separator,记录分隔符(默认是一个换行符)。
    • RSTART:由 match 函数所匹配的字符串的第一个位置。
    • RLENGTH 由 match 函数所匹配的字符串的长度。
    • SUBSEP 数组下标分隔符(默认值是 34)。

    变量示例:

    • NR 及 NF 的用法:
    # echo -e "line1 f2 f3nline2 f4 f5nline3 f6 f7" | awk '{print "Line No:"NR", No of fields:"NF, "$0="$0, "$1="$1, "$2="$2, "$3="$3}' 
    Line No:1, No of fields:3 $0=line1 f2 f3 $1=line1 $2=f2 $3=f3
    Line No:2, No of fields:3 $0=line2 f4 f5 $1=line2 $2=f4 $3=f5
    Line No:3, No of fields:3 $0=line3 f6 f7 $1=line3 $2=f6 $3=f7
    • 使用 print $NF 可以打印一行中的最后一个字段,使用 $(NF-1) 打印倒数第二个字段,以此类推:
    [root@VM_157_18_centos test]# echo -e "abc def
    123 456
    abc ggg" | awk '{print $(NF-1)}'
    abc
    123
    abc
    [root@VM_157_18_centos test]# echo -e "abc def
    123 456
    abc ggg" | awk '{print $(NF)}'
    def
    456
    ggg
    • 打印完整行、行的第一个和最后一个字段:
    [root@VM_157_18_centos test]# echo -e "abc def
    123 456
    abc ggg" | awk '{print $0, $1, $(NF)}'
    abc def abc def
    123 456 123 456
    abc ggg abc ggg
    • 统计行数(使用 END 语句块):
    [root@VM_157_18_centos test]# echo -e "abc def
    123 456
    abc ggg" | awk 'END{print NR}'
    3

    2. 传入外部变量到 awk

    两种方式向 awk 传入变量:通过 -v 选项每次指定一个变量,或在 awk 的命令行参数中的语句块之后传入空格分隔的变量。

    [root@VM_157_18_centos test]# echo | awk -v v1=100 -v v2=200 '{print v1, v2}'
    100 200
    [root@VM_157_18_centos test]# echo | awk '{print v1, v2}' v1=100 v2=200
    100 200

    2.3 awk 模式和操作

    awk 脚本是由模式和操作组成。

    2.3.1 awk 可以使用的模式有四种:

    1. 正则表达式:/regular expression/

    语法:

    awk '/reg/{commands} {commands}'

    工作流程:

    示例:

    # echo -e "abc 
    123
    666" | awk '/[0-9]/{a=$0}{printf  a} '
    123666
    # echo -e "abc 
    123
    666" | awk '/[0-9]/{print $0} '
    123
    666
    # echo -e "abc 
    123
    666" | awk '/[0-9]/{print $1} '
    123
    666

    2. 关系表达式:使用运算符进行操作,可以是字符串或数字的比较测试。

    可以判断行内的某个字段是否满足条件,满足则打印数据。支持的运算符有:==!=><>=<=

    [root@VM_120_242_centos test]# echo -e 'abc ok
    1234
    xxx ok' | awk '$2=="ok"'
    abc ok
    xxx ok

    3. 模式匹配表达式:用运算符 ~(匹配)和 ~!(不匹配)。

    模式匹配时,可以将行内的某个字段使用正则表达式匹配。~ 表示开始模式匹配且打印匹配成功的行。

    [root@VM_120_242_centos test]# echo -e 'abc ok
    1234
    xxx ok' | awk '$2 ~ /ok/'
    abc ok
    xxx ok

    4. BEGIN 语句块、pattern 语句块、END 语句块

    语法:

    awk 'BEGIN{ commands } { commands } END{ commands }' file

    工作流程:

    1. 执行 BEGIN{ commands } 语句块中的语句,BEGIN 关键字可以省略。
    2. 从文件或标准输入(stdin)读取一行后执行 { commands } 语句块,然后读下一行并再次执行 { commands } 语句块,直到最后一行。
    3. 当读至文件或输入流末尾时,执行 END{ commands } 语句块,END 关键字可以省略。

    BEGIN 语句块用于只需执行一次的初始化等操作,比如变量初始化、打印输出表格的表头等。

    END 语句块用于只需执行一次的清理操作,比如文件读取完毕后打印所有行的分析结果。

    示例,其中 echo 输出的内容通过 -e 选项配合 实现换行:

    # echo -e "abd 
     1234" | awk 'BEGIN {print "Start"}  {print} END {print "END"}'
    Start
    abd 
     1234
    END
    # echo -e "abd 
     1234" | awk '{print "Start"}  {print} {print "END"}'
    Start
    abd 
     1234
    END

    2.3.2 操作

    操作位于大括号内,由一个或多个命令、函数、表达式组成,用换行符分号分隔。模式匹配成功后,执行后面的操作。例如上面的 awk '/[0-9]/{a=$0}{printf a} ' 中的 {a=$0}{printf a} 就是操作。

    2.4 输入输出

    2.4.1 getline

    从输入中每次获取一行输入。可以通过 while 循环,读完所有行。

    1. expression | getline [var]

    将管道前命令输出的结果作为 getline 的输入,每次读取一行。其中管道前的命令需要用双引号,例如 "cat 1.txt | getline var"。如果后面跟有 var,则将读取的内容保存到 var 变量中,否则会重新设置 $0 和 NF。

    示例:

    [root@VM_157_18_centos test]# cat 1.txt 
    123
    hello
    hello world
    hello
    hello world
    666
    fine
    [root@VM_157_18_centos test]# awk 'BEGIN{while("cat 1.txt" | getline var) print var}'
    123
    hello
    hello world
    hello
    hello world
    666
    fine
    [root@VM_157_18_centos test]# awk 'BEGIN{while("cat 1.txt" | getline ) print $0, NF}'
    123 1
    hello 1
    hello world 2
    hello 1
    hello world 2
    666 1
    fine 1

    2. getline [var]

    从处理的文件中读取输入。同样,如果没有 var,则会设置 $0,并且这时候会更新 NF, NR 和 FNR:

    [root@VM_157_18_centos test]# awk '{while(getline var) print var}' 1.txt 
    hello
    hello world
    hello
    hello world
    666
    fine

    2.4.2 close

    close 函数可以用于关闭已经打开的文件或者管道,很少会用到。

    上面例子中 getline 函数的第一种形式用到管道,我们可以用 close 函数把这个管道关闭 close("cat statement.txt")

    2.4.3 system

    执行外部命令,例如:

    [root@VM_157_18_centos test]# awk 'BEGIN{system("uname -a")}'
    Linux VM_157_18_centos 3.10.0-693.el7.x86_64 #1 SMP Tue Aug 22 21:09:27 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

    2.5 字符串

    awk 的字符串连结操作不需要任何操作符,只要把需要连结的串并列写在一起即可。

    [root@VM_157_18_centos test]# echo | awk '{T=123;print T;T=T+"abc";print T}'
    123
    123
    [root@VM_157_18_centos test]# echo | awk '{T=123;print T;T=T"abc";print T}'
    123
    123abc
    [root@VM_157_18_centos ~]# awk 'BEGIN{a="aaa";b="666";print a"   "b}'
    aaa   666

    字符串函数

    定义

    函数 解释
    length [(String)] 返回 String 参数指定的字符串的长度(字符形式)。如果未给出 String 参数,则返回整个记录 $0 的长度。
    blength [(String)] 返回 String 参数指定的字符串的长度(以字节为单位)。如果未给出 String 参数,则返回整个记录 $0 的长度。
    substr( String, M, [ N ] ) 返回具有 N 参数指定的字符数量的子串。子串从 String 参数指定的字符串取得,其字符以 M 参数指定的位置开始。M 参数指定为将 String 参数中的第一个字符作为编号 1。如果未指定 N 参数,则子串到 String 的末尾。
    index( String1, String2 ) 在由 String1 参数指定的字符串(其中有出现 String2 指定的参数)中,返回位置,从 1 开始编号。如果 String2 参数不在 String1 参数中出现,则返回 0(零)。
    tolower( String ) 字符串中每个大写字符将更改为小写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。
    toupper( String ) 字符串中每个小写字符将更改为大写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。
    split( String, A, [Ere] ) 将 String 参数指定的参数分割为数组元素 A[1], A[2], …, A[n],并返回 n 变量的值。此分隔可以通过 Ere 参数指定的扩展正则表达式进行,或用当前字段分隔符(FS 特殊变量)来进行(如果没有给出 Ere 参数)。除非上下文指明特定的元素还应具有一个数字值,否则 A 数组中的元素用字符串值来创建。
    match( String, Ere ) 在 String 参数指定的字符串(Ere 参数指定的扩展正则表达式出现在其中)中返回位置(字符形式),从 1 开始编号,或如果 Ere 参数不出现,则返回 0(零)。RSTART 特殊变量设置为返回值。RLENGTH 特殊变量设置为匹配的字符串的长度,或如果未找到任何匹配,则设置为 -1(负一)。
    gsub( Ere, Repl, [ In ] ) 除了正则表达式所有具体值被替代这点,它和 sub 函数完全一样地执行。
    sub( Ere, Repl, [ In ] ) 用 Repl 参数指定的字符串替换 In 参数指定的字符串中的由 Ere 参数指定的扩展正则表达式的第一个具体值。sub 函数返回替换的数量。出现在 Repl 参数指定的字符串中的 &(和符号)由 In 参数指定的与 Ere 参数的指定的扩展正则表达式匹配的字符串替换。如果未指定 In 参数,缺省值是整个记录($0 记录变量)。

    示例

    [root@VM_157_18_centos ~]# awk 'BEGIN{info="hello world from awk,你好世界!";print length(info);}'
    21
    [root@VM_157_18_centos ~]# awk 'BEGIN{info="hello world from awk!";print substr(info,4,10);}'
    lo world f
    [root@VM_157_18_centos ~]# awk 'BEGIN{info="hello world from awk!";print index(info,"awk");}'
    18
    [root@VM_157_18_centos ~]# awk 'BEGIN{info="hello 666 world from 777 awk!";print match(info, /[0-9]/);}'
    7
    [root@VM_157_18_centos ~]# awk 'BEGIN{info="hello 666 world from 777 awk!";gsub(/[0-9]+/,"!",info);print info}'
    hello ! world from ! awk!

    2.6 数组

    处理文本经常使用数组。数组索引(下标)可以是数字和字符串在awk中数组叫做关联数组(associative arrays)。awk 中的数组不必提前声明,也不必声明大小。数组元素用 0 或空字符串来初始化。

    awk 中的数组下标从 1 开始,这与 C 的数组不一样。

    awk 中的数组是默认是无序的关联数组。通过 for…in 循环得到是无序数组。对于数字下标的数组,如果需要得到有序数组,需要通过下标获得。例如:

    [root@VM_157_18_centos test]# awk 'BEGIN{
     a["a"]=123;
     a[1]="abc";
     a["z"]=666;
     a["b"]=222;
     for (k in a) {
      print k, a[k];
     }
     len = length(a);
     for (i = 0; i < len; i++) {
      print i, a[i];
     }
    }'
    z 666
    a 123
    b 222
    1 abc
    0 
    1 abc
    2 
    3

    数组定义

    数字做数组索引:

    arr[1] = "a"
    arr[2] = "123"

    字符串做数组索引:

    arr["a"] = "a"
    arr["b"] = "123"

    使用中 print arr[1] 会打印出 a;使用 print arr["b"] 会得到 123。

    用 for 循环读数组的值

    { for(item in array) {print array[item]}; }       #输出的顺序是随机的
    { for(i=1;i<=len;i++) {print array[i]}; }         #Len是数组的长度

    数组内置函数

    数组长度 length

    length 函数是 awk 内置函数,用于求数组长度。

    [root@VM_157_18_centos test]# awk 'BEGIN{
     a[1]=123;
     a[2]="abc";
     print length(a);
    }'
    2

    判断键值存在以及删除键值

    [root@VM_157_18_centos ~]# awk 'BEGIN{
     a["a"]=123;
     a[1]="abc";
     a["z"]=666;
     a["b"]=222;
     if ("z" in a) {
        print "z in a"
     }
     if ("kkk" in a) {
        print "kkk in a"
     }
    }'
    z in a

    二维数组

    awk 中的二维数组用法跟 C 语言类似

    • 通过 array[i, j] 这样的形式访问。
    • 通过 if ( (i, j) in arr ) 判断元素是否存在,下标必须放置在圆括号中。
    • 通过 for ( key in arr ) 遍历数组。
    [root@VM_157_18_centos ~]# awk 'BEGIN{awk 'BEGIN{
     for (i = 0; i < 4; i++) {
      for (j = 0; j < 4; j++) {
       arr[i, j] = i * j;
       print i"*"j, "=", i * j;
      }
     }
    }'
    0*0 = 0
    0*1 = 0
    ...
    3*2 = 6
    3*3 = 9

    2.7 内置函数

    时间函数

    函数 说明
    mktime( YYYY MM dd HH MM ss[ DST]) 生成时间
    strftime([format [, timestamp]]) 格式化时间输出,将时间戳转为时间字符串格式,见下表
    systime() 时间戳,从 1970 年 1 月 1 日开始到当前时间的秒数

    strftime日期和时间格式说明符:

    格式 描述
    %a 星期几的缩写(Sun)
    %A 星期几的完整写法(Sunday)
    %b 月名的缩写(Oct)
    %B 月名的完整写法(October)
    %c 本地日期和时间
    %d 十进制日期
    %D 日期 08/20/99
    %e 日期,如果只有一位会补上一个空格
    %H 用十进制表示24小时格式的小时
    %I 用十进制表示12小时格式的小时
    %j 从1月1日起一年中的第几天
    %m 十进制表示的月份
    %M 十进制表示的分钟
    %p 12小时表示法(AM/PM)
    %S 十进制表示的秒
    %U 十进制表示的一年中的第几个星期(星期天作为一个星期的开始)
    %w 十进制表示的星期几(星期天是0)
    %W 十进制表示的一年中的第几个星期(星期一作为一个星期的开始)
    %x 重新设置本地日期(08/20/99)
    %X 重新设置本地时间(12:00:00)
    %y 两位数字表示的年(99)
    %Y 当前月份
    %Z 时区(PDT)
    %% 百分号(%)

    示例:

    [root@VM_157_18_centos ~]# awk 'BEGIN{tstamp=mktime("2018 04 20 16 28 59");print strftime("%c",tstamp);}'
    Fri 20 Apr 2018 04:28:59 PM CST
    
    [root@VM_157_18_centos ~]# awk 'BEGIN{print systime()}'
    1524212859
    
    [root@VM_157_18_centos ~]# awk 'BEGIN{print strftime("%Y-%m-%d %H:%M:%S", systime())}'
    2018-04-20 16:31:14

    算术函数

    格式 描述
    atan2( y, x ) 返回 y/x 的反正切。
    cos( x ) 返回 x 的余弦;x 是弧度。
    sin( x ) 返回 x 的正弦;x 是弧度。
    exp( x ) 返回 x 幂函数。
    log( x ) 返回 x 的自然对数。
    sqrt( x ) 返回 x 平方根。
    int( x ) 返回 x 的截断至整数的值。
    rand( ) 返回任意数字 n,其中 0 <= n < 1。
    srand( [expr] ) 将 rand 函数的种子值设置为 Expr 参数的值,或如果省略 Expr 参数则使用某天的时间。返回先前的种子值。

    获取随机数示例:

    [root@VM_157_18_centos ~]# awk 'BEGIN{srand(); print int(100*rand());}'
    66
    [root@VM_157_18_centos ~]# awk 'BEGIN{srand(); print int(100*rand());}'
    29
    [root@VM_157_18_centos ~]# awk 'BEGIN{srand(); print int(100*rand());}'
    76

    2.8 流程控制语句

    awk 的流程控制语句跟 C 语言中的类似。

    条件判断语句 if else

    if (expresion)
      {...}
    else if(expresion)
      {...}
    else
      {...}

    示例:

    [root@VM_157_18_centos test]# awk 'BEGIN{
        score=60;
        if (socre > 60) {
            print "good";
        } else if (score <= 60) {
            print "danger";
        } else {
            print "hehe";
        }      
    }'
    danger

    循环语句

    for 循环

    awk 中支持 C 风格的 for 循环:

    for (init; ;expression) {
        ...
    }

    awk 也支持 for in 循环遍历数组:

    for (var in arr) {
        ...
    }

    示例一:

    [root@VM_157_18_centos test]# awk 'BEGIN{
     for (i = 5; i > 0; i--) {
      print i;
     }
    }'
    5
    4
    3
    2
    1

    示例二:

    [root@VM_157_18_centos test]# awk 'BEGIN{
     arr[1] = "a";
     arr[2]="123";
     for (v in arr) {
      print v, arr[v];
     }
    }'
    1 a
    2 123

    while 循环

    while(expression) {
        ...
    }

    next 跳过后面的语句,continue 结束本次循环并开始下次循环

    awk 就像是 C 语言中的 for 循环,循环执行次数就是文件或输入流的行数。next 指令类似 continue,跳过本次循环,开始下一次循环。下面示例跳过包含数字的行:

    [root@VM_157_18_centos test]# echo -e 'abc
    123
    ddd
    333
    666' | awk '/[0-9]/{next}{print NR, $0}'
    1 abc
    3 ddd
  • 相关阅读:
    HDOJ 4747 Mex
    HDU 1203 I NEED A OFFER!
    HDU 2616 Kill the monster
    HDU 3496 Watch The Movie
    Codeforces 347A A. Difference Row
    Codeforces 347B B. Fixed Points
    Codeforces 372B B. Hungry Sequence
    HDU 1476 Sudoku Killer
    HDU 1987 How many ways
    HDU 2564 词组缩写
  • 原文地址:https://www.cnblogs.com/kika/p/10851672.html
Copyright © 2020-2023  润新知