getline用法详解

在默认情况下,awk支持从文件或者STDIN中读取数据。我们也可以使用getline来灵活读取数据,例如在main代码块执行过程中读取某个非待处理文件的数据,或者从某个读取某个shell命令结果数据。

getline有返回值:1:正确读取到了数据。

0:读取数据遇到EOF。

负数:读取遇到了错误。-1表示文件无法打开,-2表示IO操作需要重试。遇到错误时还会使用变量ERRNO来描述错误。

为了awk代码的健壮性,在使用getline的时候,一般会加上条件判断。if((getline)<0){...}

if((getline)<=0){...}

if((getline)>0){...}

记得将getline使用小括号包裹,否则getline<0会被识别为输入重定向而不是大小判断。

无参数的getline

getline无参数时表示立即从当前数据流(文件或者STDIN)中读取下一条记录保存至$0。做字段分割。然后从getline的位置继续向后执行awk代码。

此时getline会设置$0、位置参数($1...$NF)、NR、FNR和RT。# awk '/^1/{print;getline;print}' a.txt

1   Bob     male    28   abc@qq.com     18023394012

2   Alice   female  24   def@gmail.com  18084925203

10  Bruce   female  27   bcbd@139.com   13942943905

10  Bruce   female  27   bcbd@139.com   13942943905

记住,print省略参数表示print $0。从输出结果来看,第4行比较诡异。因为Bruce那行已经是文件的末尾,此时再getline会遇到EOF,返回值为0。$0不做修改,依然是Bruce那行。因此Bruce那行输出了两次。

所以我们最好是对getline做条件判断,增强代码健壮性。# awk '/^1/{print;if((getline)<=0){exit};print}' a.txt

1   Bob     male    28   abc@qq.com     18023394012

2   Alice   female  24   def@gmail.com  18084925203

10  Bruce   female  27   bcbd@139.com   13942943905

awk中有另外一个指令类似于getline,叫next,我们先来看执行结果。# awk '/^1/{print;next;print}' a.txt

1   Bob     male    28   abc@qq.com     18023394012

10  Bruce   female  27   bcbd@139.com   13942943905

遇到next以后,立即读取下一条记录,但是它不会像getline那样从当前位置继续往下执行代码,而是会跳出当前的awk内部循环(类似于循环语句中的continue),重新执行一遍main代码块(即要重新匹配pattern了)。由于需要重新匹配pattern,因此第一次next取得Alice行就不符合pattern,第二次next已经遇到EOF,因此就结束了。

带参数的getline

无参数的getline在获取下一条记录后将记录赋值给$0并划分字段,而带参数的getline带的是一个参数,这个参数是一个变量。带参数的getline在获取下一条记录后将记录赋值给参数变量并且不划分字段。

因此,带参数的getline只会设置NR、FNR、RT和参数变量var,不会修改$0、位置参数和NF。# awk '/^1/{print;if((getline var)<=0){exit};print var;print $0;print $2}' a.txt

1   Bob     male    28   abc@qq.com     18023394012

2   Alice   female  24   def@gmail.com  180849252031   Bob     male    28   abc@qq.com     18023394012

Bob10  Bruce   female  27   bcbd@139.com   13942943905

上面的输出结果中,即使通过getline已经处理到了Alice那行,但是$0和$2依然是上一行Bob的数据。

再来一个例子对比带参和无参getline的区别。[root@c7-server awk]# awk '/Tony/{print;getline;print $0,$2}' a.txt

3   Tony    male    21   aaa@163.com    17048792503

4   Kevin   male    21   bbb@189.com    17023929033 Kevin

[root@c7-server awk]# awk '/Tony/{print;getline var;print $0,$2}' a.txt

3   Tony    male    21   aaa@163.com    17048792503

3   Tony    male    21   aaa@163.com    17048792503 Tony

注意这里我们为了简便没有对getline的返回值做条件判断。

从指定的文件中getline

上面两种getline的用法均是从当前处理的文件中(假设没有使用STDIN,因为情况较少)读取下一条记录,不过我们使用getline的情况一般是为了在处理当前文件的过程中获取其他文件的数据进行处理。例如假设a.txt是配置文件,在处理该文件的过程中遇到了某些关键字需要追加另一个配置文件c.txt的内容,这种情况是可能存在的。

无参getline从文件中获取数据:记录保存至$0,划分字段(设置$N(即位置参数)),设置NF。由于是读取其他文件的数据,因此不设置NR和FNR。getline

从Coprocess中getline

中文协程,在英文中有两种解释,一种叫做Coroutine,另一种叫做Coprocess,它俩是不同的概念。

我们这里说的awk的协程指的是Coprocess,有协助的程序之意。要解释协程我们先来看bash中的1条命令。cmd1 | cmd2 | cmd3 ...

这个是bash的管道,管道之间的命令是同步执行的。而协程是异步执行的,形如管道。cmd1 |& cmd2cmd2 |& cmd3

这边展示的是伪代码,因为bash中实现协程使用的是bash内置命令coproc。“|&”是awk实现协程的符号。其中cmd2被称作协程程序(coprocess)。

注意这种管道也叫做双路管道(two-way pipe)。

协程的使用场景:虽然awk功能强大,但是某些功能不好用awk实现或者用户更熟悉bash下其他的命令,那么我们可以使用协程将数据由awk传递给协程处理,再由协程传递回awk。伪代码如下。awkPrint "data" |& shellCmd

shellCmd |& getline [var]

例如,假设我们不懂awk中的substr()这个取子字符串的函数,那么我们可以借助shell命令sed来取得邮箱字段的域名。

首先我们先确定sed命令。# echo "abc@qq.com" | sed -nr "s/.*@(.*)/\1/p"qq.com

代码量比较多,因此写成文件使用-f选项调用。awk中的sed中的双引号和反斜线需要使用转义。# cat getlineCoprocSed.awk BEGIN {

CMD="sed -nr \"s/.*@(.*)/\\1/p\""}

NR>1{

print $5 |& CMD    close(CMD,"to")

CMD |& getline email_domain    close(CMD)

print email_domain

}# awk -f getlineCoprocSed.awk a.txt

qq.com

... ...

139.com

代码中有两处close函数需要引起我们的注意。我们先来看看第一个close()函数。print $5 |& CMD

close(CMD,"to")

close()函数的第二个参数的值如果是to,则表示关闭向协程写入数据的管道,也可以理解为向协程写入EOF。用来标识我们已经向协程写完了数据,协程中的命令可以开始执行了(对于该案例就是sed命令)。这么做的原因是某些协程中的命令需要等待文件内容全部准备好了才可以开始执行,例如sort排序命令,无论排序的规则是什么,它想实现排序的前提条件就是要读取完全部的数据才可以,而确定自己是否读取完了文件的全部数据就是看是否遇到了EOF。如果命令需要EOF而协程中又不存在的话,命令就会阻塞在那里等待EOF。同学们可以自己尝试注释掉该close试看看。

再来看看第二个close()函数。CMD |& getline email_domain

close(CMD)

这里的close()函数虽然没有带第二个参数,其实它是省略了from,因为它是默认参数,下面两个是等价的。close(CMD)

close(CMD,"from")

它表示关闭从协程(coprocess)读取数据的管道。如果数据写入端的协程管道关闭了,数据读取端的协程管道没关闭,那么这个管道就会存在,下次即便是相同的代码也会继续使用同一个管道。我们尝试注释掉getlineCoprocess.awk中的第二个close()函数就会遇到报错。# awk -f getlineCoprocSed.awk a.txt

qq.com

awk: getlineCoprocSed.awk:6: (FILENAME=a.txt FNR=3) fatal: print: attempt to write to closed write end of two-way pipe

在NR==2时我们输出了qq.com,但是遇到NR==3的时候,由于上一条记录处理过程中我们没有关闭掉读取协程数据的管道导致这个双路管道依然存在,而这个管道的数据写入端此前已经被我们关闭了,所以遇到了这样的报错。

因此正确使用协程双路管道的方式是:向协程写入数据完毕以后要关闭写入端的管道(close(cmd,"to"))。

从协程读取数据完毕以后要关闭读取端的管道(close(cmd[,"from"]))。

我们再来看一个使用协程的例子。我们期望对a.txt文件内容按照年龄字段进行排序,输出的内容要是sort命令的输出结果,但是我们必须使用awk命令。sort -k4n a.txt

思路:awk是我们的主程序。将sort命令作为协助程序。awk内部循环将第二行开始的每一行数据发送给协程。要在数据全部发送完毕后(END代码块)再对数据进行排序,然后再循环输出排序后的数据。# cat getlineCoprocSort.awk BEGIN {

cmd="sort -k4n"}

NR==1 {

print

}

NR>1 {

print |& cmd

}

END {

close(cmd,"to")    # 这里需要close,否则协程sort会阻塞。

while(cmd |& getline){

print

}

close(cmd)    # 这里的close实测是可以不要的,因为刚好到了代码的尾部了,不过强烈不建议养成这种坏习惯!

}

这里还有一个知识点,我不太了解,但是还是列出。

如果协程中的cmd是按块缓冲的,则需要将其改变成按行缓冲,否则getline会阻塞。cmd="cmdline"cmd="stdbuf -oL cmdline"

close()函数

在awk当中,使用getline从文件或者命令结果中获取数据,文件/命令只会在第一次getline时打开/执行。当文件内容/命令结果有多条记录时,getline每次仅获取下一条记录,想让getline获取多条记录就需要使用循环。

由于getline的运行机制,当读取完数据集(文件的内容与命令的执行结果我称之为数据集比较方便)的所有记录后,getline的标记会一直停留在EOF处导致同样的文件或者命令的数据集无法被getline重新获取,要想重新获取的话就必须关闭它。关闭数据集以后,下次使用数据集才会重新打开。close("file")

close("cmd")

在从coprocess中getline的情况下,会产生一个双路管道(two-way pipe),一端向协程写入数据,另一端从协程读取数据。两端都需要关闭。awkPrint "data" |& shellCmd    # 使用close(shellCmd,"to")关闭。

shellCmd |& getline [var]    # 使用close(shellCmd,"from")关闭,可简写close(shellCmd)。

通过system()函数执行shell命令

我们可以通过管道,将需要执行的shell命令print给shell解释器来执行。# awk 'BEGIN{print "pwd" | "bash"}'

/root

# awk 'BEGIN{print "date" | "bash"}'Sat Jan  9 15:36:49 CST 2021

shell解释器可以是sh、bash等,可以先绝对路径也可以只写解释器名称。

我们也可以通过system()来执行shell命令。system()函数的返回值是shell命令的退出状态码。通过system调用的shell命令也可以包含重定向、管道之类的复杂操作。# awk 'BEGIN{system("date +\"%F %T\"")}'

2021-01-09 15:40:14# awk 'BEGIN{system("date +\"%F %T\">/dev/null")}'# awk 'BEGIN{system("date +\"%F %T\"|cat")}'

2021-01-09 15:40:52

system()在开始运行前会flush出awk的缓冲区数据。如果shell命令是空的话,那么system("")不会执行任何shell命令而只会flush缓冲。这部分的概念请参考awk内置函数fflush()。

linux getline函数用法,Linux文本处理三剑客之awk学习笔记05:getline用法详解相关推荐

  1. Linux学习笔记003----linux yum命令详解

    yum(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及SUSE中的Shell前端软件包管理器.基於RPM包管理,能够从指定的服务器自动下载RP ...

  2. linux学习笔记(3)——ll命令详解

    **ll 是 ls-s 的别名,意思为:罗列出当前文件或目录的详细信息,含有时间.读写权限.大小.时间等信息 ** drwxr-xr-x 2 root root 48 2013-11-27 16:34 ...

  3. linux学习笔记:dstat命令详解

    dstat 是一个可以取代vmstat,iostat,netstat和ifstat这些命令的多功能产品.dstat克服了这些命令的局限并增加了一些另外的功能,增加了监控项,也变得更灵活了.dstat可 ...

  4. Linux文本处理三剑客(awk、grep、sed)

    目录 grep 简介 实际使用 小结 sed awk 名字由来 强大的文本处理工具 语法 域 模式&动作 结合正则 复合表达式 printf 格式化输出 内置变量 内置函数 awk脚本 gre ...

  5. Puppeteer 学习笔记及基本用法

    Puppeteer 学习笔记及基本用法 Puppeteer 安装 语法 基本语法 API 分层结构 加载导航页面 等待元素.请求.响应 自定义等待 元素定位 用户模拟操作 请求拦截 获取 WebSoc ...

  6. 情态动词学习笔记(4) 基本用法:建议和忠告

    情态动词学习笔记(4) 基本用法:建议和忠告 could 表建议 it is a nice day. we could go for a walk. 今天天气不错,我们可以出去散步 shall用于第一 ...

  7. 翻转电平函数实现LED闪烁-STM32电控学习笔记05

    翻转电平函数实现LED闪烁-STM32电控学习笔记05 day5:2022/9/23 [函数介绍] 在前面帖子大致了解了一下HAL_GPIO_WritePin()函数和HAL_Delay()函数的用法 ...

  8. linux getenv函数 get,linux之getenv putenv setenv和unsetenv详解

    1.getenv函数 头文件:#include 函数原型: char * getenv(const char* name); 函数说明:getenv()用来取得参数name环境变量的内容. 函数参数: ...

  9. linux 打印函数宏,linux内核中的嵌入式汇编宏函数

    在看linux内核代码时,常会遇到诸如:static inline _syscall0(int,fork)这样的函数.经查阅资料,发现该函数是嵌入式汇编宏函数. linux内核提供了7个非常有用的宏定 ...

最新文章

  1. 敏捷软件开发--计划
  2. CornerNet 测试:
  3. windows计划任务启动bat执行java文件
  4. 《每日一题》62. Unique Paths 不同路径
  5. python读写文件操作_详解Python文件读写操作
  6. 接受的token无法改变_基于BCH的新Token方案SLP的原理与应用
  7. javascript中为某个对象(控件)绑定事件的几种方法
  8. 链接生成动态二维码图片显示在页面上
  9. DB,Cache和Redis应用场景分析
  10. 测试nb信号的软件_NB频点概述
  11. linux卸载nps,Linux NPS服务部署
  12. Android学习|动画——逐帧、补间、属性动画
  13. Centos 6.x 更新内核 2.6-4.13
  14. 国科大学习资料--模式识别--2018-2019期末试卷及解析(刘成林)
  15. Flutter中的多选按钮组件Checkbox
  16. 25 个超棒的 Python 脚本合集(迷你项目)
  17. 机器人素质教育,是时候普及一下了
  18. 支付宝支持给微信好友转账/ 14家自动驾驶概念股均价已蒸发80%/ 苹果M2 iPad Pro推出在即…今日更多新鲜事在此...
  19. 2022茶艺师(中级)复训题库及模拟考试
  20. 电脑WLAN连接异常:自上次连接后,某些信息已更改。我们还需要一些信息才能完成连接。

热门文章

  1. 基于JAVA+Servlet+JSP+MYSQL的学生选课管理系统
  2. 基于JAVA+SpringMVC+Mybatis+MYSQL的电影院订票系统
  3. 计算机四级准考证ppt,计算机等级考试四级课件PPT.ppt
  4. DesiredCapabilities内容详解(摘)
  5. [转载]CSS 创作指南(Beta)(css规范)
  6. FreeMarker MyEclipse IDE
  7. 程序员,你的粮草何在?
  8. [Delphi]ListView基本用法大全
  9. java引用公共类_使用键引用从Java公共类获取值 - java
  10. mac os android连接wifi密码,Mac使用小技巧:找回WiFi密码