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

it2022-05-09  38

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 f3\nline2 f4 f5\nline3 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\n123 456\nabc ggg" | awk '{print $(NF-1)}' abc 123 abc [root@VM_157_18_centos test]# echo -e "abc def\n123 456\nabc ggg" | awk '{print $(NF)}' def 456 ggg 打印完整行、行的第一个和最后一个字段: [root@VM_157_18_centos test]# echo -e "abc def\n123 456\nabc 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\n123 456\nabc 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 \n123\n666" | awk '/[0-9]/{a=$0}{printf a} ' 123666 # echo -e "abc \n123\n666" | awk '/[0-9]/{print $0} ' 123 666 # echo -e "abc \n123\n666" | awk '/[0-9]/{print $1} ' 123 666

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

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

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

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

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

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

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

语法:

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

工作流程:

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

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

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

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

# echo -e "abd \n 1234" | awk 'BEGIN {print "Start"} {print} END {print "END"}' Start abd 1234 END # echo -e "abd \n 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十进制表示的分钟%p12小时表示法(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\n123\nddd\n333\n666' | awk '/[0-9]/{next}{print NR, $0}' 1 abc 3 ddd

转载于:https://www.cnblogs.com/kika/p/10851672.html

相关资源:数据结构—成绩单生成器

最新回复(0)