ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

AWK的学习记录之三——读取输入文件

2021-04-12 09:05:58  阅读:186  来源: 互联网

标签:FS 读取 记录 之三 空格 字段 awk 分隔符 AWK


读取输入文件

在典型的awk程序中,awk从标准输入(默认情况下,这是键盘,但通常是另一个命令的管道)或在awk命令行上指定名称的文件中读取所有输入。如果指定输入文件,awk将按顺序读取它们,在处理一个文件中的所有数据之后再继续下一个文件。当前输入文件的名称可以在预定义变量FILENAME中找到(请参阅Predefined Variables一节)。

输入以称为记录的单位读取,并按程序规则一次处理一条记录。默认情况下,每条记录为一行。每一条记录都被自动分割成称为字段的块。这使得程序处理记录的各个部分更加方便。

在极少数情况下,可能需要使用getline命令。getline命令很有价值,因为它可以从任意数量的文件中进行显式输入,而且与它一起使用的文件不必在awk命令行中命名(请参阅Explicit Input with getline一节)。

1、如何将输入拆分为记录

awk将程序的输入分为记录和字段。它跟踪到目前为止从当前输入文件读取的记录数。该值存储在名为FNR的预定义变量中,每次启动新文件时,该变量都会重置为零。另一个预定义变量NR记录到目前为止从所有数据文件读取的输入记录总数。它从零开始,但从不自动重置为零。

通常,记录由换行符分隔。可以通过向内置变量RS赋值来控制记录的分隔方式。如果RS是任何单个字符,则该字符分隔记录。否则(在gawk中),RS被视为正则表达式。稍后将更详细地解释这一机制。

(1)使用标准awk拆分记录

  • 记录由一个称为记录分隔符的字符分隔。默认情况下,记录分隔符是换行符。这就是为什么默认情况下记录是单行的。要使用不同的字符作为记录分隔符,只需将该字符赋给预定义的变量RS。

  • 与任何其他变量一样,RS的值可以在awk程序中使用赋值运算符“=”进行更改(请参阅 Assignment Expressions一节)。新的记录分隔符应该用引号括起来,表示字符串常量。通常,正确的时间是在执行开始时,在处理任何输入之前,这样就可以用适当的分隔符读取第一条记录。为此,请使用特殊的开始模式(请参阅The BEGIN and END Special Patterns一节)。例如:

    awk 'BEGIN { RS = "u" }
         { print $0 }' mail-list
    

    在读取任何输入之前,将RS的值更改为'u'。新值是一个字符串,其第一个字符是字母“u”;因此,记录之间用字母“u”隔开。然后读取输入文件,awk程序中的第二条规则(没有模式的操作)打印每条记录。因为每个print语句在其输出的末尾添加了一个换行符,所以这个awk程序将输入的每个“u”都复制为一个换行符。以下是在mail-list上运行程序的结果:

    $ awk 'BEGIN { RS = "u" }
    >      { print $0 }' mail-list
    -| Amelia       555-5553     amelia.zodiac
    -| sq
    -| e@gmail.com    F
    -| Anthony      555-3412     anthony.assert
    -| ro@hotmail.com   A
    -| Becky        555-7685     becky.algebrar
    -| m@gmail.com      A
    -| Bill         555-1675     bill.drowning@hotmail.com       A
    -| Broderick    555-0542     broderick.aliq
    -| otiens@yahoo.com R
    -| Camilla      555-2912     camilla.inf
    -| sar
    -| m@skynet.be     R
    -| Fabi
    -| s       555-1234     fabi
    -| s.
    -| ndevicesim
    -| s@
    -| cb.ed
    -|     F
    -| J
    -| lie        555-6699     j
    -| lie.perscr
    -| tabor@skeeve.com   F
    -| Martin       555-6480     martin.codicib
    -| s@hotmail.com    A
    -| Sam
    -| el       555-3430     sam
    -| el.lanceolis@sh
    -| .ed
    -|         A
    -| Jean-Pa
    -| l    555-2127     jeanpa
    -| l.campanor
    -| m@ny
    -| .ed
    -|      R
    -|
    

    请注意,名称'Bill'的条目没有拆分。在原始数据文件中(有关示例,请参见Data files for the Examples一节),该行如下所示:

    Bill         555-1675     bill.drowning@hotmail.com       A
    

    它不包含'u',因此没有理由分割记录,不像其他记录,每个记录都有一个或多个'u'出现。事实上,此记录被视为前一条记录的一部分;输出中分隔它们的换行符是数据文件中的原始换行符,而不是awk打印记录时添加的换行符!

    另一种更改记录分隔符的方法是在命令行上使用变量赋值功能(请参阅Other Command-Line Arguments一节):

    awk '{ print $0 }' RS="u" mail-list
    

    这将在处理mail-list之前将RS设置为'u'

    使用字母字符(如'u')作为记录分隔符很可能会产生奇怪的结果。在大多数情况下,使用像'/'这样的不寻常字符更有可能产生正确的行为,但是没有任何保证。寓意是:了解你的数据。

2、检查字段

  • 当awk读取一条输入记录时,awk实用程序会自动将该记录解析或分离为称为字段的块。默认情况下,字段用空格隔开,就像一行中的单词一样。awk中的空白表示一个或多个空格、制表符或换行符的任何字符串;其他语言认为是空白的字符(如formfeed、垂直制表符等)不被awk认为是空白。

  • 字段的目的是使你更方便地引用这些记录。你不必使用它们,你可以对整个记录进行操作,如果你想的话,但是字段是使简单的awk程序如此强大的原因。

  • 在awk程序中使用美元符号('$')来引用字段,后跟所需字段的编号。因此,$1表示第一个字段,$2表示第二个字段,依此类推。(与Unix shell不同,字段号不限于个位数。$127是记录中的第127个字段。)例如,假设下面是一行输入:

    This seems like a pretty nice example.
    
  • 这里第一个字段,或者$1,是'This',第二个字段,或者$2,是'seen',依此类推。请注意,最后一个字段$7是'example.'。因为'e''.'之间没有空格,所以句号'.'被认为是第七个字段的一部分。

  • NF是一个预定义变量,其值是当前记录中的字段数。awk每次读取记录时都会自动更新NF的值。无论有多少字段,记录中的最后一个字段都可以用$NF表示。所以,$NF$7是一样的,这就是'example.'。如果尝试引用上一个字段之外的字段(例如$8,而记录只有七个字段),则会得到空字符串。(如果用于数字运算,则为零。)

  • $0的使用看起来像是对'zeroth'字段的引用,这是一种特殊情况:它表示整个输入记录。当你对特定领域不感兴趣时就使用它。下面是更多的例子:

    $ awk '$1 ~ /li/ { print $0 }' mail-list
    -| Amelia       555-5553     amelia.zodiacusque@gmail.com    F
    -| Julie        555-6699     julie.perscrutabor@skeeve.com   F
    

    此示例打印文件mail-list中第一个字段包含字符串'li'的每条记录。

    相比之下,以下示例在整个记录中查找'li',并打印每个匹配输入记录的第一个和最后一个字段:

    $ awk '/li/ { print $1, $NF }' mail-list
    -| Amelia F
    -| Broderick R
    -| Julie F
    -| Samuel A
    

3、非恒定字段号

  • 字段编号不必是常量。awk语言中的任何表达式都可以用在'$'后面来表示字段。表达式的值指定字段号。如果值是字符串而不是数字,则会将其转换为数字。考虑这个例子:

    awk '{ print $NR }'
    
  • 回想一下,NR是到目前为止读取的记录数:第一个记录中有一个,第二个记录中有两个,依此类推。所以这个例子打印第一条记录的第一个字段,第二条记录的第二个字段,依此类推。对于第二十条记录,将打印字段编号20;很可能,该记录的字段少于20个,因此将打印一个空行。下面是将表达式用作字段号的另一个示例:

    awk '{ print $(2*2) }' mail-list
    
  • awk计算表达式(2*2)并将其值用作要打印的字段的编号。'*'表示乘法,因此表达式'2*2'的计算结果为4。使用圆括号是为了在'$'运算之前完成乘法;只要字段数表达式中有二进制运算符21,就必须使用圆括号。然后,本例为文件mail-list的每一行打印关系类型(第四个字段)。(所有awk操作符都按优先级递减的顺序列在 Operator Precedence (How Operators Nest)中。)

  • 如果计算的字段号为零,则得到整个记录。因此,'$(2-2)'的值与$0相同。不允许使用负字段号;尝试引用一个字段号通常会终止程序。(POSIX标准没有定义引用负字段号时发生的情况。gawk注意到这一点并终止您的程序。其他awk实现的行为可能有所不同。)

  • Examining Fields中所述,awk将当前记录的字段数存储在内置变量NF中(另请参阅 Predefined Variables一节)。因此,表达式$NF不是一个特殊的特性,它是计算NF并将其值用作字段号的直接结果。

4、更改字段内容

  • 待续

5、指定字段的分隔方式

  • 字段分隔符是单个字符或正则表达式,它控制awk将输入记录拆分为字段的方式。awk扫描输入记录中与分隔符匹配的字符序列;字段本身就是匹配项之间的文本。

  • 在下面的示例中,我们使用项目符号(•)表示输出中的空格。如果字段分隔符为'oo',则以下行:

    moo goo gai pan
    

    分为三个字段:'m''•g''•gai•pan'。请注意第二个和第三个字段的值中的前导空格。

    字段分隔符由预定义的变量FS表示。Shell程序员注意:awk不使用POSIX兼容Shell(如unixbourneshell、sh或Bash)使用的IFS名称。

  • FS的值可以在awk程序中用赋值运算符'='来更改(参见 Assignment Expressions一节)。通常,正确的时间是在处理任何输入之前的执行开始时,以便使用适当的分隔符读取第一条记录。为此,请使用特殊的开始模式(请参阅The BEGIN and END Special Patterns一节)。例如,这里我们将FS的值设置为字符串“,”

    awk 'BEGIN { FS = "," } ; { print $2 }'
    

    给定输入行:

    John Q. Smith, 29 Oak St., Walamazoo, MI 42139
    

    这个awk程序提取并打印字符串'•29•Oak•St.'

    有时输入数据包含分隔符,这些分隔符不会按所认为的方式分隔字段。例如,我们刚才使用的示例中的人名可能会附加标题或后缀,例如:

    John Q. Smith, LXIX, 29 Oak St., Walamazoo, MI 42139
    

    相同的程序将提取'•LXIX'而不是'•29•Oak•St.'。如果您希望程序打印地址,您会感到惊讶。其寓意是谨慎选择数据布局和分隔符,以防止出现此类问题。(如果数据的格式不便于处理,您可以先使用单独的awk程序进行处理。)

(1)空格通常作为分隔字段

  • 字段通常由空格序列(空格、制表符和换行符)分隔,而不是由单个空格分隔。一行中的两个空格不能分隔空字段。字段分隔符FS的默认值是包含单个空格' '的字符串。如果awk以通常的方式解释这个值,那么每个空格字符将分隔字段,因此一行中的两个空格将在它们之间形成一个空字段。这种情况不会发生的原因是单个空格作为FS的值是一种特殊情况,它用于指定字段的默认分隔方式。
  • 如果FS是任何其他单个字符,如',',则该字符的每次出现将分隔两个字段。两个连续的事件限定了一个空字段。如果字符出现在行首或行尾,也会划出一个空字段。空格字符是唯一不遵循这些规则的单个字符。

(2)使用正则表达式分隔字段

  • 上一小节讨论了使用单个字符或简单字符串作为FS的值。更一般地,FS的值可以是包含任何正则表达式的字符串。在这种情况下,正则表达式记录中的每个匹配项都会分隔字段。例如,任务:

    FS = ", \t"
    

    使输入行中由逗号后跟空格和制表符组成的每个区域成为字段分隔符。

    对于一个不那么简单的正则表达式示例,请尝试使用单个空格来分隔字段,就像使用单个逗号一样。FS可以设置为'[ ]'(左括号、空格、右括号)。这个正则表达式只匹配一个空格而不匹配其他任何空格(请参阅 Regular Expressions一节)。

    'FS=" "'(单个空格)和'FS="[\t\n]+"'(匹配一个或多个空格、制表符或换行符的正则表达式)这两种情况之间有一个重要的区别。对于FS的两个值,字段由空格、制表符和/或换行符的行(多个相邻引用)分隔。但是,当FS的值为" "时,awk首先从记录中去掉前导和尾随空格,然后决定字段的位置。例如,以下管道打印'b'

    $ echo ' a b c d ' | awk '{ print $2 }'
    -| b
    

    但是,此管道打印'a'(注意每个字母周围的额外空格):

    $ echo ' a  b  c  d ' | awk 'BEGIN { FS = "[ \t\n]+" }
    >                                  { print $2 }'
    -| a
    

    在本例中,第一个字段为null或空。

    每当重新计算$0时,前导和尾随空格的剥离也会起作用。例如,研究这个管道:

    $ echo '   a b c d' | awk '{ print; $2 = $2; print }'
    -|    a b c d
    -| a b c d
    

    第一个print语句在读取记录时打印该记录,前导空格保持不变。对$2的赋值通过将$1$NF串联在一起重新生成$0,并用OFS的值(默认情况下是一个空格)分隔。因为在查找$1时忽略了前导空格,所以它不是新$0的一部分。最后,最后一个print语句打印新的$0

(3)使每个字符成为单独的字段

  • 有时您可能需要分别检查记录的每个字符。这可以在gawk中通过简单地将空字符串(“”)赋给FS来完成。(c.e.)在这种情况下,记录中的每个字符都成为一个单独的字段。例如:

    $ echo a b | gawk 'BEGIN { FS = "" }
    >                  {
    >                      for (i = 1; i <= NF; i = i + 1)
    >                          print "Field", i, "is", $i
    >                  }'
    -| Field 1 is a
    -| Field 2 is
    -| Field 3 is b
    

    传统上,没有定义FS等于“”的行为。在这种情况下,Unix awk的大多数版本只是将整个记录视为只有一个字段。(d.c.)在兼容模式下(请参阅 Command-Line Options一节),如果FS是空字符串,那么gawk的行为也是这样的。

(4)从命令行设置FS

  • 可以在命令行上设置FS。使用-F选项执行此操作。例如:

    awk -F, 'program' input-files
    

    将FS设置为“,”字符。请注意,该选项使用大写的'F'而不是小写的'f'。后一个选项(-f)指定包含awk程序的文件。

    用于-F参数的值的处理方式与预定义变量FS的赋值方式完全相同。字段分隔符中的任何特殊字符都必须正确转义。例如,要在命令行上使用'\'作为字段分隔符,必须键入:

    # same as FS = "\\"
    awk -F\\\\ '…' files …
    

    因为'\'用于在shell中引用,awk会看到'-F\\'。然后awk处理转义字符的'\\'(参见 Escape Sequences一节),最后产生一个'\'作为字段分隔符。

    作为一种特殊情况,在兼容模式下(请参阅Command-Line Options一节),如果-F的参数为't',则FS设置为制表符。如果在shell中键入'-F\t',不加引号,'\'将被删除,因此,awk认为您确实希望字段用制表符而不是't'分隔。如果确实希望字段用't'分隔,请在命令行上使用'-v FS=“t”''-F"[t]"'。如果不在兼容模式下,请使用'-F'\t''指定制表符分隔字段。

    例如,让我们使用一个名为edu.awk包含模式/edu/和动作'print $1'的:

    /edu/   { print $1 }
    

    我们还将FS设置为'-'字符,并在文件mail-list上运行程序。以下命令打印在大学工作或就读的人员的姓名列表,以及他们电话号码的前三位数字:

    $ awk -F- -f edu.awk mail-list
    -| Fabius       555
    -| Samuel       555
    -| Jean
    

    注意输出的第三行。原始文件中的第三行如下所示:

    Jean-Paul    555-2127     jeanpaul.campanorum@nyu.edu     R
    

    作为姓名的一部分的'-'被用作字段分隔符,而不是最初预期的电话号码中的'-'。这说明了为什么在选择字段和记录分隔符时必须小心。

    在处理Unix系统密码文件时,最常用的字段分隔符可能是单个字符。在许多Unix系统上,每个用户在系统密码文件中都有一个单独的条目,每个用户只有一行。这些行中的信息用冒号分隔。第一个字段是用户的登录名,第二个字段是用户的加密或影子密码。(影子密码由第二个字段中的单个'x'表示。)密码文件条目可能如下所示:

    arnold:x:2076:10:Arnold Robbins:/home/arnold:/bin/bash
    

    以下程序搜索系统密码文件并打印未显示全名的用户的条目:

    awk -F: '$5 == ""' /etc/passwd
    

(5)使整行成为单个字段

  • 有时,将整个输入行视为单个字段是有用的。只需将FS设置为'\n'(换行符),就可以轻松且可移植地完成此操作:
    awk -F'\n' 'program' files …
    
    当你这样做时,$1等于$0
  • 后面的内容用到再说把把把!!!

标签:FS,读取,记录,之三,空格,字段,awk,分隔符,AWK
来源: https://blog.csdn.net/qq_33740167/article/details/115584572

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有