zoukankan      html  css  js  c++  java
  • 彻底理解 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
  • 相关阅读:
    [LeetCode] 94. Binary Tree Inorder Traversal 二叉树的中序遍历
    [LeetCode] 103. Binary Tree Zigzag Level Order Traversal 二叉树的之字形层序遍历
    Notepad++ Shortcuts 快捷键
    IplImage 与 QImage 相互转换
    [LeetCode] 105. Construct Binary Tree from Preorder and Inorder Traversal 由先序和中序遍历建立二叉树
    QMessageBox 使用方法
    Qt5 和 Qt4 的一些改动和不同
    Qt5.4 VS2010 Additional Dependancies
    [LeetCode] Construct Binary Tree from Inorder and Postorder Traversal 由中序和后序遍历建立二叉树
    [LeetCode] Best Time to Buy and Sell Stock IV 买卖股票的最佳时间之四
  • 原文地址:https://www.cnblogs.com/kika/p/10851672.html
Copyright © 2011-2022 走看看