标签 awk 下的文章

让我们开始使用它。

awk 是用于 Unix 和类 Unix 系统的强大文本解析工具,但是由于它有可编程函数,因此你可以用它来执行常规解析任务,因此它也被视为一种编程语言。你可能不会使用 awk 开发下一个 GUI 应用,并且它可能不会代替你的默认脚本语言,但是它是用于特定任务的强大程序。

这些任务或许是惊人的多样化。了解 awk 可以解决你的哪些问题的最好方法是学习 awk。你会惊讶于 awk 如何帮助你完成更多工作,却花费更少的精力。

awk 的基本语法是:

awk [options] 'pattern {action}' file

首先,创建此示例文件并将其保存为 colours.txt

name       color  amount
apple      red    4
banana     yellow 6
strawberry red    3
grape      purple 10
apple      green  8
plum       purple 2
kiwi       brown  4
potato     brown  9
pineapple  yellow 5

数据被一个或多个空格分隔为列。以某种方式组织要分析的数据是很常见的。它不一定总是由空格分隔的列,甚至可以不是逗号或分号,但尤其是在日志文件或数据转储中,通常有一个可预测的格式。你可以使用数据格式来帮助 awk 提取和处理你关注的数据。

打印列

awk 中,print 函数显示你指定的内容。你可以使用许多预定义的变量,但是最常见的是文本文件中以整数命名的列。试试看:

$ awk '{print $2;}' colours.txt
color
red
yellow
red
purple
green
purple
brown
brown
yellow

在这里,awk 显示第二列,用 $2 表示。这是相对直观的,因此你可能会猜测 print $1 显示第一列,而 print $3 显示第三列,依此类推。

要显示全部列,请使用 $0

美元符号($)后的数字是表达式,因此 $2$(1+1) 是同一意思。

有条件地选择列

你使用的示例文件非常结构化。它有一行充当标题,并且各列直接相互关联。通过定义条件,你可以限定 awk 在找到此数据时返回的内容。例如,要查看第二列中与 yellow 匹配的项并打印第一列的内容:

awk '$2=="yellow"{print $1}' file1.txt
banana
pineapple

正则表达式也可以工作。此表达式近似匹配 $2 中以 p 开头跟上任意数量(一个或多个)字符后继续跟上 p 的值:

$ awk '$2 ~ /p.+p/ {print $0}' colours.txt
grape   purple  10
plum    purple  2

数字能被 awk 自然解释。例如,要打印第三列包含大于 5 的整数的行:

awk '$3>5 {print $1, $2}' colours.txt
name    color
banana  yellow
grape   purple
apple   green
potato  brown

字段分隔符

默认情况下,awk 使用空格作为字段分隔符。但是,并非所有文本文件都使用空格来定义字段。例如,用以下内容创建一个名为 colours.csv 的文件:

name,color,amount
apple,red,4
banana,yellow,6
strawberry,red,3
grape,purple,10
apple,green,8
plum,purple,2
kiwi,brown,4
potato,brown,9
pineapple,yellow,5

只要你指定将哪个字符用作命令中的字段分隔符,awk 就能以完全相同的方式处理数据。使用 --field-separator(或简称为 -F)选项来定义分隔符:

$ awk -F"," '$2=="yellow" {print $1}' file1.csv
banana
pineapple

保存输出

使用输出重定向,你可以将结果写入文件。例如:

$ awk -F, '$3>5 {print $1, $2} colours.csv > output.txt

这将创建一个包含 awk 查询内容的文件。

你还可以将文件拆分为按列数据分组的多个文件。例如,如果要根据每行显示的颜色将 colours.txt 拆分为多个文件,你可以在 awk 中包含重定向语句来重定向每条查询

$ awk '{print > $2".txt"}' colours.txt

这将生成名为 yellow.txtred.txt 等文件。

在下一篇文章中,你将了解有关字段,记录和一些强大的 awk 变量的更多信息。

本文改编自社区技术播客 Hacker Public Radio


via: https://opensource.com/article/19/10/intro-awk

作者:Seth Kenlon 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

用一个简单的 AWK 程序跟踪你的同事喝咖啡的欠款。

以下基于一个真实的故事,虽然一些名字和细节有所改变。

很久以前,在一个遥远的地方,有一间(划掉)办公室。由于各种原因,这个办公室没有购买速溶咖啡。所以那个办公室的一些人聚在一起决定建立“咖啡角”。

咖啡角的一名成员会购买一些速溶咖啡,而其他成员会付给他钱。有人喝咖啡比其他人多,所以增加了“半成员”的级别:半成员每周允许喝的咖啡限量,并可以支付其它成员支付的一半。

管理这事非常操心。而我刚读过《Unix 编程环境》这本书,想练习一下我的 AWK 编程技能,所以我自告奋勇创建了一个系统。

第 1 步:我用一个数据库来记录成员及其应支付给咖啡角的欠款。我是以 AWK 便于处理的格式记录的,其中字段用冒号分隔:

member:john:1:22
member:jane:0.5:33
member:pratyush:0.5:17
member:jing:1:27

上面的第一个字段标识了这是哪一种行(member)。第二个字段是成员的名字(即他们的电子邮件用户名,但没有 @ )。下一个字段是其成员级别(成员 = 1,或半会员 = 0.5)。最后一个字段是他们欠咖啡角的钱。正数表示他们欠咖啡角钱,负数表示咖啡角欠他们。

第 2 步:我记录了咖啡角的收入和支出:

payment:jane:33
payment:pratyush:17
bought:john:60
payback:john:50

Jane 付款 $33,Pratyush 付款 $17,John 买了价值 $60 的咖啡,而咖啡角还款给 John $50。

第 3 步:我准备写一些代码,用来处理成员和付款,并生成记录了新欠账的更新的成员文件。

#!/usr/bin/env --split-string=awk -F: -f

释伴行(#!)需要做一些调整,我使用 env 命令来允许从释伴行传递多个参数:具体来说,AWK 的 -F 命令行参数会告诉它字段分隔符是什么。

AWK 程序就是一个规则序列(也可以包含函数定义,但是对于这个咖啡角应用来说不需要)

第一条规则读取该成员文件。当我运行该命令时,我总是首先给它的是成员文件,然后是付款文件。它使用 AWK 关联数组来在 members 数组中记录成员级别,以及在 debt 数组中记录当前欠账。

$1 == "member" {
   members[$2]=$3
   debt[$2]=$4
   total_members += $3
}

第二条规则在记录付款(payment)时减少欠账。

$1 == "payment" {
   debt[$2] -= $3
}

还款(payback)则相反:它增加欠账。这可以优雅地支持意外地给了某人太多钱的情况。

$1 == "payback" {
   debt[$2] += $3
}

最复杂的部分出现在有人购买(bought)速溶咖啡供咖啡角使用时。它被视为付款(payment),并且该人的债务减少了适当的金额。接下来,它计算每个会员的费用。它根据成员的级别对所有成员进行迭代并增加欠款

$1 == "bought" {
   debt[$2] -= $3
   per_member = $3/total_members
   for (x in members) {
       debt[x] += per_member * members[x]
   }
}

END 模式很特殊:当 AWK 没有更多的数据要处理时,它会一次性执行。此时,它会使用更新的欠款数生成新的成员文件。

END {
   for (x in members) {
       printf "%s:%s:%s\n", x, members[x], debt[x]
   }
}

再配合一个遍历成员文件,并向人们发送提醒电子邮件以支付他们的会费(积极清账)的脚本,这个系统管理咖啡角相当一段时间。


via: https://opensource.com/article/19/2/drinking-coffee-awk

作者:Moshe Zadka 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

得到我的 awk 秘籍。

我管理着一个个人网站,用手工编辑网站上的网页。由于网站上的页面并不多,这种方法对我很适合,可以让我对网站代码的细节一清二楚。

最近我升级了网站的设计样式,我决定把所有的普通引号都转换成“花引号”,即在打印材料中使用的那种引号:用 “” 来代替 ""。

手工修改所有的引号太耗时了,因此我决定将这个转换所有 HTML 文件中引号的过程自动化。不过通过程序或脚本来实现该功能需要费点劲。这个脚本需要知道何时将普通引号转换成花引号,并决定使用哪种引号(LCTT 译注:左引号还是右引号,单引号还是双引号)。

有多种方法可以转换引号。Greg Pittman 写过一个 Python 脚本 来修正文本中的花引号。而我自己使用 GNU awk (gawk) 来实现。

下载我的 awk 秘籍。免费下载

开始之前,我写了一个简单的 gawk 函数来评估单个字符。若该字符是一个引号,这该函数判断是输出普通引号还是花引号。函数查看前一个字符;若前一个字符是空格,则函数输出左花引号。否则函数输出右花引号。脚本对单引号的处理方式也一样。

function smartquote (char, prevchar) {
        # print smart quotes depending on the previous character
        # otherwise just print the character as-is

        if (prevchar ~ /\s/) {
                # prev char is a space
                if (char == "'") {
                        printf("‘");
                }
                else if (char == "\"") {
                        printf("“");
                }
                else {
                        printf("%c", char);
                }
        }
        else {
                # prev char is not a space
                if (char == "'") {
                        printf("’");
                }
                else if (char == "\"") {
                        printf("”");
                }
                else {
                        printf("%c", char);
                }
        }
}

这个 gawk 脚本的主体部分通过该函数处理 HTML 输入文件的一个个字符。该脚本在 HTML 标签内部逐字原样输出所有内容(比如,<html lang="en">)。在 HTML 标签外,脚本使用 smartquote() 函数来输出文本。smartquote() 函数来评估是输出普通引号还是花引号。

function smartquote (char, prevchar) {
        ...
}

BEGIN {htmltag = 0}

{
        # for each line, scan one letter at a time:

        linelen = length($0);

        prev = "\n";

        for (i = 1; i <= linelen; i++) {
                char = substr($0, i, 1);

                if (char == "<") {
                        htmltag = 1;
                }

                if (htmltag == 1) {
                        printf("%c", char);
                }
                else {
                        smartquote(char, prev);
                        prev = char;
                }

                if (char == ">") {
                        htmltag = 0;
                }
        }

        # add trailing newline at end of each line
        printf ("\n");
}

下面是一个例子:

gawk -f quotes.awk test.html > test2.html

其输入为:

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Test page</title>
  <link rel="stylesheet" type="text/css" href="/test.css" />
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width" />
</head>
<body>
  <h1><a href="/"><img src="logo.png" alt="Website logo" /></a></h1>
  <p>"Hi there!"</p>
  <p>It's and its.</p>
</body>
</html>

其输出为:

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Test page</title>
  <link rel="stylesheet" type="text/css" href="/test.css" />
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width" />
</head>
<body>
  <h1><a href="/"><img src="logo.png" alt="Website logo" /></a></h1>
  <p>&ldquo;Hi there!&rdquo;</p>
  <p>It&rsquo;s and its.</p>
</body>
</html>

via: https://opensource.com/article/18/8/gawk-script-convert-smart-quotes

作者:Jim Hall 选题:lujun9972 译者:lujun9972 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Q:我想在 Linux / 类Unix 系统上使用 awk 打印文件名。 如何使用 awk 的 BEGIN 特殊模式打印文件名? 我可以使用 gawk/awk 打印当前输入文件的名称吗?(LCTT 译注:读者最好能有一些 awk 的背景知识,否则阅读本文的时候会有一些困惑)

FILENAME 变量中存放着当前输入文件的名称。 您可以使用 FILENAME 显示或打印当前输入文件名,如果在命令行中未指定文件,则 FILENAME 的值为 - (标准输入)(LCTT 译注:多次按下回车键即可看到效果)。 但是,除非由 getline 设置,否则 FILENAMEBEGIN 特殊模式中未定义。

使用 awk 打印文件名

使用语法如下:

awk '{ print FILENAME }' fileNameHere 
awk '{ print FILENAME }' /etc/hosts

因 awk 逐行读取文件,因此,你可能看到多个文件名,为了避免这个情况,你可以使用如下的命令:(LCTT 译注:FNR 表示当前记录数,只在文件中有效)

awk 'FNR == 1{ print FILENAME } ' /etc/passwd 
awk 'FNR == 1{ print FILENAME } ' /etc/hosts

使用 awk 的 BEGIN 特殊规则打印文件名

使用下面的语法:(LCTT 译注:ARGV[I] 表示输入的第 i 个参数)

awk 'BEGIN{print ARGV[1]}' fileNameHere 
awk 'BEGIN{print ARGV[1]}{ print "someting or do something on data" }END{}' fileNameHere 
awk 'BEGIN{print ARGV[1]}' /etc/hosts

示例输出:

/etc/hosts

然而,ARGV[1] 并不是每一次都能奏效,例如:

ls -l /etc/hosts | awk 'BEGIN{print ARGV[1]} { print }'

你需要将它修改如下(假设 ls -l 只产生一行输出):

ls -l /etc/hosts | awk '{ print "File: " $9 ", Owner:" $3 ", Group: " $4 }'

示例输出:

File: /etc/hosts, Owner:root, Group: root

处理由通配符指定的多个文件名

使用如下的示例语法:

awk '{ print FILENAME; nextfile } ' *.c 
awk 'BEGIN{ print "Starting..."} { print FILENAME; nextfile }END{ print "....DONE"} ' *.conf

示例输出:

Starting...
blkid.conf
cryptconfig.conf
dhclient6.conf
dhclient.conf
dracut.conf
gai.conf
gnome_defaults.conf
host.conf
idmapd.conf
idnalias.conf
idn.conf
insserv.conf
iscsid.conf
krb5.conf
ld.so.conf
logrotate.conf
mke2fs.conf
mtools.conf
netscsid.conf
nfsmount.conf
nscd.conf
nsswitch.conf
openct.conf
opensc.conf
request-key.conf
resolv.conf
rsyncd.conf
sensors3.conf
slp.conf
smartd.conf
sysctl.conf
vconsole.conf
warnquota.conf
wodim.conf
xattr.conf
xinetd.conf
yp.conf
....DONE

nextfile 告诉 awk 停止处理当前的输入文件。 下一个输入记录读取来自下一个输入文件。 更多信息,请参见 awk/gawk 命令手册页:

man awk 
man gawk

关于作者

作者是 nixCraft 的创立者,也是经验丰富的系统管理员和 Linux/Unix shell 脚本的培训师。 他曾与全球各行各业的客户合作,涉及 IT,教育,国防和空间研究以及非营利部门等多个行业。 您可以在 TwitterFacebookGoogle+上关注他。 可以通过订阅我的 RSS 来获取更多的关于系统管理,Linux/Unix ,和开源主题的相关资料。


via: https://www.cyberciti.biz/faq/how-to-print-filename-with-awk-on-linux-unix/

作者:Vivek Gite 译者:amwps290 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

从 awk 系列开始直到第 12 部分,我们都是在命令行或者脚本文件里写一些简短的 awk 命令和程序。

然而 awk 和 shell 一样也是一个解释型语言。通过从开始到现在的一系列的学习,你现在能写可以执行的 awk 脚本了。

和写 shell 脚本差不多,awk 脚本以下面这一行开头:

#! /path/to/awk/utility -f

例如在我的系统上,awk 工具安装在 /user/bin/awk 目录,所以我的 awk 脚本以如下内容作为开头:

#! /usr/bin/awk -f

上面一行的解释如下:

  • #! ,称为 释伴 Shebang ,指明使用那个解释器来执行脚本中的命令
  • /usr/bin/awk ,即解释器
  • -f ,解释器选项,用来指定读取的程序文件

说是这么说,现在从下面的简单例子开始,让我们深入研究一些可执行的 awk 脚本。使用你最喜欢的编辑器创建一个新文件,像下面这样:

$ vi script.awk

然后把下面代码粘贴到文件中:

#!/usr/bin/awk -f
BEGIN { printf "%s\n","Writing my first awk executable script!" }

保存文件后退出,然后执行下面命令,使得脚本可执行:

$ chmod +x script.awk

然后,执行它:

$ ./script.awk

输出样例:

Writing my first awk executable script!

一个严格的程序员一定会问:“注释呢?”。是的,你可以在 awk 脚本中包含注释。在代码中写注释是一种良好的编程习惯。

它有利于其它程序员阅读你的代码,理解程序文件或者脚本中每一部分的功能。

所以,你可以像下面这样在脚本中增加注释:

#!/usr/bin/awk -f
# 这是如何在 awk 中写注释的示例
# 使用特殊模式 BEGIN 来输出一句话
BEGIN { printf "%s\n","Writing my first awk executable script!" }

接下来我们看一个读文件的例子。我们想从帐号文件 /etc/passwd 中查找一个叫 aaronkilik 的用户,然后像下面这样打印用户名、用户的 ID、用户的 GID (LCTT译注:组 ID):

下面是我们脚本文件的内容,文件名为 second.awk。

#! /usr/bin/awk -f
# 使用 BEGIN 指定字符来设定 FS 内置变量
BEGIN { FS=":" }
# 搜索用户名 aaronkilik 并输出账号细节
/aaronkilik/ { print "Username :",$1,"User ID :",$3,"User GID :",$4 }

保存文件后退出,使得脚本可执行,然后像下面这样执行它:

$ chmod +x second.awk
$ ./second.awk /etc/passwd

输出样例:

Username : aaronkilik User ID : 1000 User GID : 1000

在下面最后一个例子中,我们将使用 do while 语句来打印数字 0-10:

下面是我们脚本文件的内容,文件名为 do.awk。

#! /usr/bin/awk -f
#printing from 0-10 using a do while statement
#do while statement
BEGIN {
#initialize a counter
x=0
do {
print x;
x+=1;
}
while(x<=10)
}

保存文件后,像之前操作一样使得脚本可执行。然后,运行它:

$ chmod +x do.awk
$ ./do.awk

输出样例

0
1
2
3
4
5
6
7
8
9
10

总结

我们已经到达这个精彩的 awk 系列的最后,我希望你从整个 13 个章节中学到了很多知识,把这些当作你 awk 编程语言的入门指导。

我一开始就提到过,awk 是一个完整的文本处理语言,所以你可以学习很多 awk 编程语言的其它方面,例如环境变量、数组、函数(内置的或者用户自定义的),等等。

awk 编程还有其它内容需要学习和掌握,所以在文末我提供了一些重要的在线资源的链接,你可以利用他们拓展你的 awk 编程技能。但这不是必须的,你也可以阅读一些关于 awk 的书籍。

如果你任何想要分享的想法或者问题,在下面留言。记得保持关注我们,会有更多的精彩内容。


via: http://www.tecmint.com/write-shell-scripts-in-awk-programming/

作者:Aaron Kili 译者:chunyang-wen 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

当你回顾所有到目前为止我们已经覆盖的 awk 实例,从 awk 系列的开始,你会注意到各种实例的所有指令是顺序执行的,即一个接一个地执行。但在某些情况下,我们可能希望基于一些条件进行文本过滤操作,即流程控制语句允许的那些语句。

在 awk 编程中有各种各样的流程控制语句,其中包括:

  • if-else 语句
  • for 语句
  • while 语句
  • do-while 语句
  • break 语句
  • continue 语句
  • next 语句
  • nextfile 语句
  • exit 语句

然而,对于本系列的这一部分,我们将阐述:if-elseforwhiledo while 语句。请记住,我们已经在这个 awk 系列的第 6 部分介绍过如何使用 awk 的 next 语句。

1. if-else 语句

如你想的那样。if 语句的语法类似于 shell 中的 if 语句:

if  (条件 1) {
     动作 1
}
else {
      动作 2
}

在上述语法中,条件 1条件 2 是 awk 表达式,而动作 1动作 2 是当各自的条件得到满足时所执行的 awk 命令。

条件 1 满足时,意味着它为真,那么动作 1 被执行并退出 if 语句,否则动作 2 被执行。

if 语句还能扩展为如下的 if-else_if-else 语句:

if (条件 1){
     动作 1
}
else if (条件 2){
      动作 2
}
else{
     动作 3
}

对于上面的形式,如果条件 1 为真,那么动作 1 被执行并退出 if 语句,否则条件 2 被求值且如果值为真,那么动作 2 被执行并退出 if 语句。然而,当条件 2 为假时,那么动作 3 被执行并退出 if 语句

这是在使用 if 语句的一个实例,我们有一个用户和他们年龄的列表,存储在文件 users.txt 中。

我们要打印一个清单,显示用户的名称和用户的年龄是否小于或超过 25 岁。

aaronkilik@tecMint ~ $ cat users.txt
Sarah L         35      F
Aaron Kili      40      M
John  Doo       20      M
Kili  Seth      49      M

我们可以写一个简短的 shell 脚本来执行上文中我们的工作,这是脚本的内容:

#!/bin/bash
awk ' {
        if ( $3 <= 25 ){
           print "User",$1,$2,"is less than 25 years old." ;
        }
        else {
           print "User",$1,$2,"is more than 25 years old" ;
        }
}'    ~/users.txt

然后保存文件并退出,按如下方式使脚本可执行并运行它:

$ chmod +x test.sh
$ ./test.sh

输出样例

User Sarah L is more than 25 years old
User Aaron Kili is more than 25 years old
User John Doo is less than 25 years old.
User Kili Seth is more than 25 years old

2. for 语句

如果你想在一个循环中执行一些 awk 命令,那么 for 语句为你提供一个做这个的合适方式,格式如下:

for ( 计数器的初始化 ; 测试条件 ; 计数器增加 ){
      动作
}

这里,该方法是通过一个计数器来控制循环执行来定义的,首先你需要初始化这个计数器,然后针对测试条件运行它,如果它为真,执行这些动作并最终增加这个计数器。当计数器不满足条件时,循环终止。

在我们想要打印数字 0 到 10 时,以下 awk 命令显示 for 语句是如何工作的:

$ awk 'BEGIN{ for(counter=0;counter<=10;counter++){ print counter} }'

输出样例

0
1
2
3
4
5
6
7
8
9
10

3. while 语句

while 语句的传统语法如下:

while ( 条件 ) {
          动作
}

这个条件是一个 awk 表达式而动作是当条件为真时被执行的 awk 命令。

下面是一个说明使用 while 语句来打印数字 0 到 10 的脚本:

#!/bin/bash
awk ' BEGIN{ counter=0;
        while(counter<=10){
              print counter;
              counter+=1;
        }
}'

保存文件并使脚本可执行,然后运行它:

$ chmod +x test.sh
$ ./test.sh

输出样例

0
1
2
3
4
5
6
7
8
9
10

4. do while 语句

它是上文中 while 语句的一个变型,具有以下语法:

do {
     动作
}
 while (条件)

这轻微的区别在于,在 do while 语句下,awk 的命令在求值条件之前执行。使用上文 while 语句的例子,我们可以通过按如下所述修改 test.sh 脚本中的 awk 命令来说明 do while 语句的用法:

#!/bin/bash

awk ' BEGIN{ counter=0;
        do{
            print counter;
            counter+=1;
        }
        while (counter<=10)
}'

修改脚本之后,保存文件并退出。按如下方式使脚本可执行并执行它:

$ chmod +x test.sh
$ ./test.sh

输出样例

0
1
2
3
4
5
6
7
8
9
10

总结

这不是关于 awk 的流程控制语句的一个全面的指南,正如我早先提到的,在 awk 里还有其他几个流程控制语句。

尽管如此,awk 系列的这一部分使应该你明白了一个明确的基于某些条件控制的 awk 命令是如何执行的基本概念。

你还可以了解其余更多的流程控制语句以获得更多关于该主题的理解。最后,在 awk 的系列下一节,我们将进入编写 awk 脚本。


via: http://www.tecmint.com/use-flow-control-statements-with-awk-command/ 作者:Aaron Kili 译者:robot527 校对:wxy

本文由 LCTT 原创编译,Linux 中国 荣誉推出