什么是Awk

Awk是一种小巧的编程语言及命令行工具。(其名称得自于它的创始人Alfred Aho、Peter Weinberger 和 Brian Kernighan姓氏的首个字母)。它非常适合服务器上的日志处理,主要是因为Awk可以对文件进行操作,通常以可读文本构建行。

我说它适用于服务器是因为日志文件,转储文件(dump files),或者任意文本格式的服务器终止转储到磁盘都会变得很大,并且在每个服务器你都会拥有大量的这类文件。如果你经历过这样的情境——在没有像Splunk或者其他等价的工具情况下不得不在50个不同的服务器里分析几G的文件,你会觉得去获取和下载所有的这些文件并分析他们是一件很糟糕的事。

我亲身经历过这种情境。当一些Erlang节点将要死掉并留下一个700MB到4GB的崩溃转储文件(crash dump)时,或者当我需要在一个小的个人服务器(叫做VPS)上快速浏览日志,查找一个常规模式时。

在任何情况下,Awk都不仅仅只是用来查找数据的(否则,grep或者ack已经足够使用了)——它同样使你能够处理数据并转换数据。

代码结构

Awk脚本的代码结构很简单,就是一系列的模式(pattern)和行为(action):

  1. # comment
  2. Pattern1 { ACTIONS; }
  3.  
  4. # comment
  5. Pattern2 { ACTIONS; }
  6.  
  7. # comment
  8. Pattern3 { ACTIONS; }
  9.  
  10. # comment
  11. Pattern4 { ACTIONS; }

扫描文档的每一行时都必须与每一个模式进行匹配比较,而且一次只匹配一个模式。那么,如果我给出一个包含以下内容的文件:

this is line 1

this is line 2

this is line 1 这行就会与Pattern1进行匹配。如果匹配成功,就会执行ACTIONS。然后this is line 1 会和Pattern2进行匹配。如果匹配失败,它就会跳到Pattern3进行匹配,以此类推。

一旦所有的模式都匹配过了,this is line 2 就会以同样的步骤进行匹配。其他的行也一样,直到读取完整个文件。

简而言之,这就是Awk的运行模式

数据类型

Awk仅有两个主要的数据类型:字符串和数字。即便如此,Awk的字符串和数字还可以相互转换。字符串能够被解释为数字并把它的值转换为数字值。如果字符串不包含数字,它就被转换为0.

它们都可以在你代码里的ACTIONS部分使用 = 操作符给变量赋值。我们可以在任意时刻、任意地方声明和使用变量,也可以使用未初始化的变量,此时他们的默认值是空字符串:“”。

最后,Awk有数组类型,并且它们是动态的一维关联数组。它们的语法是这样的:var[key] = value 。Awk可以模拟多维数组,但无论怎样,这是一个大的技巧(big hack)。

模式

可以使用的模式分为三大类:正则表达式、布尔表达式和特殊模式。

正则表达式和布尔表达式

你使用的Awk正则表达式比较轻量。它们不是Awk下的PCRE(但是gawk可以支持该库——这依赖于具体的实现!请使用 awk

–version查看),然而,对于大部分的使用需求已经足够了:

  1. /admin/ { ... } # any line that contains 'admin'
  2. /^admin/ { ... } # lines that begin with 'admin'
  3. /admin$/ { ... } # lines that end with 'admin'
  4. /^[0-9.]+ / { ... } # lines beginning with series of numbers and periods
  5. /(POST|PUT|DELETE)/ # lines that contain specific HTTP verbs

注意,模式不能捕获特定的组(groups)使它们在代码的ACTIONS部分执行。模式是专门匹配内容的。

布尔表达式与PHP或者Javascript中的布尔表达式类似。特别的是,在awk中可以使用&&(“与”)、||(“或”)、!(“非”)操作符。你几乎可以在所有类C语言中找到它们的踪迹。它们可以对常规数据进行操作。

与PHP和Javascript更相似的特性是比较操作符,==,它会进行模糊匹配(fuzzy matching)。因此“23”字符串等于23,”23″ == 23 表达式返回true。!= 操作符同样在awk里使用,并且别忘了其他常见的操作符:>,<,>=,和<=。

你同样可以混合使用它们:布尔表达式可以和常规表达式一起使用。 /admin/ || debug == true 这种用法是合法的,并且在遇到包含“admin”单词的行或者debug变量等于true时该表达式就会匹配成功。

注意,如果你有一个特定的字符串或者变量要与正则表达式进行匹配,~ 和!~ 就是你想要的操作符。 这样使用它们:string ~ /regex/ 和 string !~ /regex/。

同样要注意的是,所有的模式都只是可选的。一个包含以下内容的Awk脚本:

{ ACTIONS }

对输入的每一行都将会简单地执行ACTIONS。

特殊的模式

在Awk里有一些特殊的模式,但不是很多。

第一个是BEGIN,它仅在所有的行都输入到文件之前进行匹配。这是你可以初始化你的脚本变量和所有种类的状态的主要地方。

另外一个就是END。就像你可能已经猜到的,它会在所有的输入都被处理完后进行匹配。这使你可以在退出前进行清除工作和一些最后的输出。

最后一类模式,要把它进行归类有点困难。它处于变量和特殊值之间,我们通常称它们为域(Field)。而且名副其实。

使用直观的例子能更好地解释域:

  1. # According to the following line
  2. #
  3. # $1 $2 $3
  4. # 00:34:23 GET /foo/bar.html
  5. # _____________ _____________/
  6. # $0
  7.  
  8. # Hack attempt?
  9. /admin.html$/ && $2 == "DELETE" {
  10. print "Hacker Alert!";
  11. }

域(默认地)由空格分隔。$0 域代表了一整行的字符串。 $1 域是第一块字符串(在任何空格之前), $2 域是后一块,以此类推。

一个有趣的事实(并且是在大多是情况下我们要避免的事情),你可以通过给相应的域赋值来修改相应的行。例如,如果你在一个块里执行 $0 = “HAHA THE LINE IS GONE”,那么现在下一个模式将会对修改后的行进行操作而不是操作原始的行。其他的域变量都类似。

行为

这里有一堆可用的行为(possible actions),但是最常用和最有用的行为(以我的经验来说)是:

  1. { print $0; } # prints $0. In this case, equivalent to 'print' alone
  2. { exit; } # ends the program
  3. { next; } # skips to the next line of input
  4. { a=$1; b=$0 } # variable assignment
  5. { c[$1] = $2 } # variable assignment (array)
  6.  
  7. { if (BOOLEAN) { ACTION }
  8. else if (BOOLEAN) { ACTION }
  9. else { ACTION }
  10. }
  11. { for (i=1; i<x; i++) { ACTION } }
  12. { for (item in c) { ACTION } }

这些内容将会成为你的Awk工具箱的主要工具,在你处理日志之类的文件时你可以随意地使用它们。

Awk里的变量都是全局变量。无论你在给定的块里定义什么变量,它对其他的块都是可见的,甚至是对每一行都是可见的。这严重限制了你的Awk脚本大小,不然他们会造成不可维护的可怕结果。请编写尽可能小的脚本。

函数

可以使用下面的语法来调用函数:

{ somecall($2) }

这里有一些有限的内置函数可以使用,所以我可以给出这些函数的通用文档(regular documentation)

用户定义的函数同样很简单:

  1. # function arguments are call-by-value
  2. function name(parameter-list) {
  3. ACTIONS; # same actions as usual
  4. }
  5.  
  6. # return is a valid keyword
  7. function add1(val) {
  8. return val+1;
  9. }

特殊变量

除了常规变量(全局的,可以在任意地方使用),这里还有一系列特殊的变量,它们的的作用有点像配置条目(configuration entries):

  1. BEGIN { # Can be modified by the user
  2. FS = ","; # Field Separator
  3. RS = "n"; # Record Separator (lines)
  4. OFS = " "; # Output Field Separator
  5. ORS = "n"; # Output Record Separator (lines)
  6. }
  7. { # Can't be modified by the user
  8. NF # Number of Fields in the current Record (line)
  9. NR # Number of Records seen so far
  10. ARGV / ARGC # Script Arguments
  11. }

我把可修改的变量放在BEGIN里,因为我更喜欢在那重写它们。但是这些变量的重写可以放在脚本的任意地方然后在后面的行里生效。

示例

以上的就是Awk语言的核心内容。我这里没有大量的例子,因为我趋向于使用Awk来完成快速的一次性任务。

不过我依然有一些随身携带的脚本文件,用来处理一些事情和测试。我最喜欢的一个脚本是用来处理Erlang的崩溃转储文件,形如下面的:

  1. =erl_crash_dump:0.3
  2. Tue Nov 18 02:52:44 2014
  3. Slogan: init terminating in do_boot ()
  4. System version: Erlang/OTP 17 [erts-6.2] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]
  5. Compiled: Fri Sep 19 03:23:19 2014
  6. Taints:
  7. Atoms: 12167
  8. =memory
  9. total: 19012936
  10. processes: 4327912
  11. processes_used: 4319928
  12. system: 14685024
  13. atom: 339441
  14. atom_used: 331087
  15. binary: 1367680
  16. code: 8384804
  17. ets: 382552
  18. =hash_table:atom_tab
  19. size: 9643
  20. used: 6949
  21. ...
  22. =allocator:instr
  23. option m: false
  24. option s: false
  25. option t: false
  26. =proc:<0.0.0>
  27. State: Running
  28. Name: init
  29. Spawned as: otp_ring0:start/2
  30. Run queue: 0
  31. Spawned by: []
  32. Started: Tue Nov 18 02:52:35 2014
  33. Message queue length: 0
  34. Number of heap fragments: 0
  35. Heap fragment data: 0
  36. Link list: [<0.3.0>, <0.7.0>, <0.6.0>]
  37. Reductions: 29265
  38. Stack+heap: 1598
  39. OldHeap: 610
  40. Heap unused: 656
  41. OldHeap unused: 468
  42. Memory: 18584
  43. Program counter: 0x00007f42f9566200 (init:boot_loop/2 + 64)
  44. CP: 0x0000000000000000 (invalid)
  45. =proc:<0.3.0>
  46. State: Waiting
  47. ...
  48. =port:#Port<0.0>
  49. Slot: 0
  50. Connected: <0.3.0>
  51. Links: <0.3.0>
  52. Port controls linked-in driver: efile
  53. =port:#Port<0.14>
  54. Slot: 112
  55. Connected: <0.3.0>
  56. ...

产生下面的结果:

  1. $ awk -f queue_fun.awk $PATH_TO_DUMP
  2. MESSAGE QUEUE LENGTH: CURRENT FUNCTION
  3. ======================================
  4. 10641: io:wait_io_mon_reply/2
  5. 12646: io:wait_io_mon_reply/2
  6. 32991: io:wait_io_mon_reply/2
  7. 2183837: io:wait_io_mon_reply/2
  8. 730790: io:wait_io_mon_reply/2
  9. 80194: io:wait_io_mon_reply/2
  10. ...

这是在Erlang进程里运行的函数列表,它们导致了mailboxe变得很庞大。脚本在这:

  1. # Parse Erlang Crash Dumps and correlate mailbox size to the currently running
  2. # function.
  3. #
  4. # Once in the procs section of the dump, all processes are displayed with
  5. # =proc:<0.M.N> followed by a list of their attributes, which include the
  6. # message queue length and the program counter (what code is currently
  7. # executing).
  8. #
  9. # Run as:
  10. #
  11. # $ awk -v threshold=$THRESHOLD -f queue_fun.awk $CRASHDUMP
  12. #
  13. # Where $THRESHOLD is the smallest mailbox you want inspects. Default value
  14. # is 1000.
  15. BEGIN {
  16. if (threshold == "") {
  17. threshold = 1000 # default mailbox size
  18. }
  19. procs = 0 # are we in the =procs entries?
  20. print "MESSAGE QUEUE LENGTH: CURRENT FUNCTION"
  21. print "======================================"
  22. }
  23.  
  24. # Only bother with the =proc: entries. Anything else is useless.
  25. procs == 0 && /^=proc/ { procs = 1 } # entering the =procs entries
  26. procs == 1 && /^=/ && !/^=proc/ { exit 0 } # we're done
  27.  
  28. # Message queue length: 1210
  29. # 1 2 3 4
  30. /^Message queue length: / && $4 >= threshold { flag=1; ct=$4 }
  31. /^Message queue length: / && $4 < threshold { flag=0 }
  32.  
  33. # Program counter: 0x00007f5fb8cb2238 (io:wait_io_mon_reply/2 + 56)
  34. # 1 2 3 4 5 6
  35. flag == 1 && /^Program counter: / { print ct ":", substr($4,2) }

你跟上思路没?如果跟上了,你已经了解了Awk。恭喜!

原文:http://www.techug.com/awk

【转】Linux强大命令 Awk 20分钟入门介绍的更多相关文章

  1. linux常用命令 awk命令

    awk命令 awk [选项] '条件1{动作1} 条件2{动作2}...' 文件名 条件(Pattern) *) 一般使用关系表达式作为条件 *) x>10 判断变量x是否大于10 *) x&g ...

  2. Quick BI功能篇之(一):20分钟入门

    前言: 最近小编帮助隔壁团队一个小姐姐解决了个大难题:给老板汇报业绩分析,频次提高.效率提升,还得保证团队中的小伙伴们都得有点大数据时代的基本数据能力.小编觉得这么好的经验可以分享给更多志同道合的朋友 ...

  3. Linux Shell 命令--awk

    说明: awk被设计用于数据流,能够对列和行进行操作.而sed更多的是匹配,进行替换和删除.awk有很多内建的功能,比如数组,函数等.灵活性是awk的最大优势.  awk的结构}{i++}END{pr ...

  4. Linux常用命令awk

    awk能够处理类似csv这种按行格式的数据,对每一行record按照-F指定的分隔符切割,然后处理.默认支持空格和\t分隔符 1.统计文件里某一列数据等于某个值的个数 -0_djt10.txt 2.拼 ...

  5. 【Linux高频命令专题(20)】du

    概述 显示每个文件和目录的磁盘使用空间. 命令格式 du [选项][文件] 文件缺省就代表当前目录大小 参数 -a或-all 显示目录中个别文件的大小. -b或-bytes 显示目录或文件大小时,以b ...

  6. 20分钟入门Redux

    Redux就是个数据中心,不依附于任何框架在哪使用都行.但是和它最搭配的应该就是React了,而且大家学习它的动力大多也是解决React状态管理的问题.都说Redux文档详尽清晰,但我感觉并不友好,它 ...

  7. Linux rpm 命令参数使用详解[介绍和应用]

    RPM是RedHat Package Manager(RedHat软件包管理工具)类似Windows里面的“添加/删除程序” rpm 执行安装包二进制包(Binary)以及源代码包(Source)两种 ...

  8. Linux常用命令及Shell的简单介绍

    一.linux命令   1.查看指令的参数搭配: man 指令名称   2.基础指令 ls  列出当前目录下的所有文档的名称(文档指的是文件和文件夹) 常用参数搭配: ls -l 列出文档详细信息 l ...

  9. 教老婆学Linux运维(二)Linux常用命令指南【下】

    目录 tips:紧接上一篇,Linux常用命令指南[上] 2.4 文件解压缩 2.4.1 官宣的linux压缩工具:tar tar的基本命令格式为 tar [参数选项] [文件或目录] 我们前面学的命 ...

随机推荐

  1. AutoTile 自动拼接(五) 学习与实践

    今天不讲 权值检索,考虑到后期 自动拼接 做出来 更好玩,操作更方便.所以 今天我 补充一节, 网格计算与操作. 具体就是这么个效果,和地图编辑器一样,不过图块还是没有自然的拼接,这个一定一定是 下一 ...

  2. hdu1285 确定比赛名次(拓扑排序)

    确定比赛名次 Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 65536/32768K (Java/Other) Total Submis ...

  3. java 接口的回调

    Example6_3.java interface ShowMessage { void 显示商标(String s); } class TV implements ShowMessage { pub ...

  4. 转:用​C​语​言​的​r​a​n​d​(​)​和​s​r​a​n​d​(​)​产​生​伪​随​机​数​的​方​法​总​结

    标准库<cstdlib>(被包含于<iostream>中)提供两个帮助生成伪随机数的函数: 函数一:int rand(void): 从srand (seed)中指定的seed开 ...

  5. HDU 5240 Exam

    The 2015 ACM-ICPC China Shanghai Metropolitan Programming Contest 2015ACM-ICPC上海大都会赛 签到题 #include< ...

  6. opencv2 矩阵方式 resize图像缩放代码(转载)

    http://blog.sina.com.cn/s/blog_74a459380101r0yx.html opencv2 矩阵方式 resize图像缩放代码(转载) (2014-05-16 09:55 ...

  7. springmvc传递json数据到前台显示

    需要两个包 jackson-core-asl, jackson-mapper-asl controller @RequestMapping(value="/findEduList" ...

  8. 用telnet命令,POP3接收邮件

    昨天已经成功利用telnet命令发送了邮件,今天接着来,只能发送不能接收多郁闷. 邮件的接收这里是基于pop3协议的,pop3协议共定义了12条与接收相关的邮件,如下面简单解释: 首先是与登陆验证相关 ...

  9. kill -QUIT <pid>

    On Solaris and Linux a thread dump is also printed if the J2SE process receives a QUIT signal. So ki ...

  10. iis7支持asp(访问页面,页面存在仍然提示404)

    1. win7下安装IIS时ASP一般被默认不选中的状态,因此需要打开IIS检查功能视图栏中是否存在ASP选项,若没有则需要从控制面板->程序和 功能->打开或关闭Windows功能-&g ...