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
工作流程:
- 执行
BEGIN{ commands }
语句块中的语句,BEGIN 关键字可以省略。 - 从文件或标准输入(stdin)读取一行后执行
{ commands }
语句块,然后读下一行并再次执行{ commands }
语句块,直到最后一行。 - 当读至文件或输入流末尾时,执行
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