第七章 gawk功能:gawk编程
7.1.1 数字和字符串常量
    初始化和类型强制  在awk中,变量不需要定义就可以直接使用,使用一个变量就是对变量的定义。变量的类型可以试数字、字符串,或者两者都有。在赋值的时候,等号又变表达式的类型就是变量的类型。
    根据使用的不同,未初始化的变量的值为0或者空白字符串“”
    强制字符串转换为数字:
    强制数字转换为字符串:
    所有split函数建立的域和数组元素都被认为是字符串变量,除非它值包含数字值,如果域或者数组元素为空(null),那么它们的值就是null。一个空行也被看作是一个空的字符串。
7.1.2 自定义变量
    自定义变量由字母、数字和下划线组成,但是不能以数字开头。awk中的变量不需要声明。awk根据表达式中变量的内容来确定变量的类型。如果变量没有初始 化,awk就初始化字符串变量的值为NULL,数值变量值为0。如果有必要,awk能把数字变量转换为字符串变量,或者相反。变量通过复制符号被赋值。
    递增和递减操作符
    命令行自定义变量  可以在命令行中给变量赋值,然后再把这个变量传输给awk脚本。
    例如,# awk -F: -f awkscript month=4 year=2000 filename     注:变量定义要在filename前面
    -v选项(awk)。awk的-v选项允许BEGIN语句处理命令行参数。对于每一个从命令行传输的参数,他们前面都必须有-v选项
    域变量 域变量的用法类似于自定义变量,只是域变量引用的域不同。新的域可以通过赋值建立。一个域如果已被参考但是还没有赋值,它的值就是空字符串。一旦与的值改变了,就是用当前作为域分隔符的OSF的值重新计算$0变量。通常域的总数限制在100以内。
    例如,# awk '$4 == "CA" { $4 = "California"; print}' filename
    如果第四个域的值($4)等于CA,awk就把第四个域赋值为 "California"。注意,一定要有双引号,否则系统就会误认为是自定义变量而赋值为NULL.
    内建变量  内建变量的名字是由大写字母组成。它们事先被赋值并可以在表达式中使用。 
                      
                    内建变量清单
    变量名                      变量内容
     ARGC                       命令行参数的数量
     ARGIND                     命令行正在处理的当前文件的AGV的索引(仅在gawk中有效)
     ARGV                       命令行参数数组
     CONVFMT                    转换数字格式(仅在gawk中有效),默认是%.6g
     ENVIRON                    从shell传递来的包含当前环境变量的数组
     ERRNO                      当使用close函数或者通过getline函数读取的时候,发生的重新定向错误
                                的描述信息就保存在这个变量中
     FIELDWIDTHS                在对记录进行固定域宽的分割时,可以替代FS的分隔符的列表
     FILENAME                   当前的输入文件名
     FNR                        当前文件的记录号
     FS                         输入域分隔符,默认是空格
     IGNORECASE                 在正则表达式和字符串操作中关闭大小写敏感
     NF                         当前文件域的数量
     NR                         当前文件的记录数
     OFMT                       数字输出格式
     OFS                        输出域分隔符
     ORS                        输出记录分隔符
     RLENGTH                    通过match函数匹配的字符串的长度
     RS                         输入记录分隔符
     RSTART                     通过match函数匹配的字符串的偏移量
     RT                         记录结束符在输入文本的时候,gawk把它设为与RS相同
     SUBSEP                     下标分隔符
7.1.3 BEGIN 模块
    BEGIN模块后面紧跟着动作块,这个动作块在awk处理任何输入文件行之前执行。事实上,BEGIN块可以在没有任何输入文件的条件下测试,因为在 BEGIN块执行完毕以前awk不读取任何输入文件。BEGIN块通常被用来改变内建变量的值,例如OFS,RS及FS等等。
7.1.4 END模块
    END模块不匹配任何输入文件,但是执行动作块中的所有动作。END模块在整个输入晚间处理完毕后被处理。
7.2  重新定向和管道
7.2.1 输出文件的重新定向
    重新定向awk的输出到一个UNIX文件,需要使用Shell重新定向符,文件名必须被双引号括起来。
7.2.2 输入重新定向(getline)
    getline函数。 getline函数的作用是从标准输入、管道或者当前正在处理的文件之外的其他输入晚间获得输入。它负责从输入获得下一行的内容,并给NF、NR和FNR 等内建变量赋值。如果得到一个记录,getline函数就返回1,如果倒到文件的末尾就返回0。如果出现错误,例如打开文件失败,就返回-1。
7.3 管道
    如果你在awk程序中打开一个管道,那么在打开下一个管道之前必须关闭它。管道符号右边可以通过双引号关闭管道。在同一时刻只能有一个管道存在。
7.4 关闭文件和管道
    如果你打算在awk程序中再次使用文件、管道来进行读或者写,则先要去关闭它,因为知道脚本结束它都不会自动关闭。一旦打开,管道就保持开的状态知道awk退出。所以END模块中的语句对于管道也是有效的,END模块的第一行语句就是关闭管道。
    system函数。内建的system函数把Linux命令作为参数执行这些命令,然后返回退出状态值给awk程序。这有一点像C标准中的system()函数。Linux的命令必须被双引号引用。如果system的参数是一个空字符串,那么输出缓冲区就被刷新。
    例如,{
            system( "cat" $1 )
            system( "clear" )
          }
    fflush函数。 用来刷新输出缓冲区。如果没有参数,fflush函数就刷新标准的输出的缓冲区。如果fflush的参数是空字符串,例如fflush(""),fflush就帅新所有文件和管道的输出缓冲区。
7.6 条件语句
if/else else if
7.7循环
while for
7.8 程序控制语句
7.8.1 next 语句
    next语句从输入文件中读取下一行,然后从头开始执行awk脚本。
    例如(In Script)
{
  if ( $1 ~ /Peter/){next}
  else {print}
}
7.8.2 exit 语句
    exit语句用于结束awk程序。它终止对记录的处理,但是不会略过END模块,如果exit语句被赋予一个0~255之间的参数(例如 exit 1),这个参数就被打印到命令行,以判断退出成功还是失败。
例如(In Script)
    {exit(1)}
(The Command Line)
# echo $status (csh,tcsh)
1
# echo $?(bash,sh,ksh,tcsh)
1

7.9 数组
    因为awk中数组的小标可以试数字和字母,所以称为关联数组(associative array)。数组的下标(subscript)通常称为关键字(key)并且跟相应元素的值有关系。值和关键字都存储在内部的一张对问题中的关键字和值 应用散列运算法则的表格里(也就是哈希表)。由于在哈希表中使用的技术,数组元素不是按照顺序存储的。如果显示数组的内容就会发现,它们并不是按照你预料 的顺序显示出来的。
    数组跟变量 一样,都是在使用的时候自动创建,awk可以自己判断其存储的是数字还是字符串。根据内容的不同,awk的数组初始值是数字0或空字符串。你不需要声明awk数组的大小。awk数组用来从记录中收集信息,可以用于计算总和、统计单词以及跟踪模板被匹配的次数等等。
7.9.1 下标与关联数组
    用变量作为数组索引。  变量可以作为数组下标值的索引,该变量的值可以试数字或者字符串。
    special for循环。   special for循环在for循环一旦无法发挥 作用的时候,用于循环读取关联数组中的元素。当下标是字符串或者是不连续的数字时,special for循环把下标作为关键字访问跟其关联的值。
    用字符串作为下标。 下标可以试文字或者包含字符串的变量。如果下标是文字,那么必须用双引号括起来。
    用域值作为数组的下标。 任何表达式都可以用作数组的下标,域当然也可以。示例7.52中的程序计算所有在第二个域中的名字出现的次数并介绍了一种新的for循环方式。
    for(inde_value in array) statement
    数组和split函数。 awk的内建函数split允许你把一个字符串分隔为单词并存储在数组中。你可以自己定义域分隔符或者使用现在FS(域分隔符)的值。
    delete函数。 该函数用于删除数组元素。
    awk的多维数组
7.9.2 awk的命令参数处理
    AGRV。对于awk和gawk来说,可以合法地在命令行上使用内建数组ARGV。这个数组包括命令本身,担不是所有可传递到awk的选项。数组ARGV的索引从0开始。
    ARGC。 ARGC是一个内建的、包含命令行参数个数的变量。awk并不把选项作为参数。
7.10 awk内建函数
7.10.1 字符串函数
    sub和gsub函数。sub函数匹配记录中最大、最靠左边的子字符串的正则表达式,并用替换字符串替换这些字符串。如果目标字符串已经指定,正则表达式匹配目标字符串中最大的、最靠左的子字符串,并用替换字符串替换它。如果没有指定目标字符串就默认使用这个记录。
格式:
    sub(regular expression, subsitution string);
    sub(regular expression, subsitution string, target string);
gsub函数则使得在所有正则表达式被匹配的时候都发生替换。
    gsub(regular expression, subsitution string);
    gsub(regular expression, subsitution string, target string);
    index函数。 inedex函数返回子字符串第一次被匹配的位置,偏移量从位置1开始。
格式:
    index(string, substring)
例如:
# awk '{ print index("hollow", "low")}' filename
4
说明:返回子字符串low在hollow中的位置,偏移量从1开始。
    length函数。length函数返回没有参数的字符串的长度。length函数返回整个记录中的字符数。
    stustr函数。substr函数返回从位置1开始的字符串的子字符串。如果子字符串的长度给定,就返回字符串的一部分。如果指定的长度超过实际长度,就返回整个字符串。
格式:
    substr(string, starting position)
    substr(string, starting position, length of string)
    match函数。 match函数返回在字符串中正则表达式位置的索引,如果找不到指定的正则表达式就返回0。match函数设置内建变量RSTART为字符串中子字符串的开始位置,RLENGTH为到子字符串末尾的字符个数。substr函数可以用这些变量来提取模式(pattern)。
格式:
    match(string, regular expression)
例如:
    # awk 'END{start=match("Good ole USA", /[A-Z] $/); print start,RSTART, RLENGTH}' filename
    10 10 3
说明:
    正则表达式/[A-Z] $/表示在字符串的末尾搜索连续的大写字母。在字符串"Good ole USA"的第十个位置找到字符串“USA”。
如果没有字符串匹配就返回0。
    toupper和tolower函数(仅在gawk中有效)。toupper函数把字符串中所有的小写字母都变为大写,而非字母的字符则不改变。同理,tolower函数把字符串中所有的大写字母都变成小写,而非字母的字符也不改变。
格式:
    toupper(string)
    tolower(string)
    split函数。split函数使用作为第三个参数的域分隔符把字符串分割为一个数组。如果第三个参数没有提供,awk就默认使用当前FS值。
格式:
    split (string, array, field separator)
    split (string, array)
    sprintf函数。springf函数返回指定格式的表达式。这个函数允许你使用printf函数的格式说明符。
格式:
    variable=sprintf("string with format specifiers ", expr1, expr2, ..., exprn)
7.10.2 时间函数
    gawk提供了两个分别用来获取时间及格式化时间戳的函数。它们是systime和strftime函数。
    systime函数。 systime函数返回从1970年1月1日(称为元年)开始到当前的时间(不计闰年)的整秒数。
格式:
    systime()
 
    strftime函数。 strftime函数使用C库中的strftime函数格式化时间。格式说明符的格式是%T或%D等等,时间戳的格式跟systime函数返回值是一样的。如果时间戳被忽略,则默认使用当前时间。
7.10.3 内建数学函数
atan2/cos/exp/int/log/rand/sin/sqrt/srand
7.11 自定义函数
    自定义函数可以放在脚本中任何可以防止模板和动作的地方。
格式:
    function name ( parameter, parameter, parameter, ...) {
        statements
        retrun expression
      (The return statement and expression are optional )
}
    给函数中本地变量传递值。只使用变量的拷贝。数组通过地址或者指针传递,所以可以在函数内部直接改变数组元素的值。函数内部使用的任何没有作为参数传递的 变量都被看做是全局变量,也就是这些变量对于整个程序都是可见的。如果变量再函数中发生了改变,那么就是在整个程序中发生了改变。唯一向函数提供本地变量 的办法就是把他们放在参数列表中,这些参数通常被放在列表的最后。如果函数调用的时候没有提供正式的参数,那么参数就初始化为空。return语句通常就 返回程序控制并向函数调用者返回一个值。

7.13 其他细节
    从磁带或电子表格中读取的数据也许没有明显域分隔符,但是数据有固定的列宽,可以用substr函数预处理这些数据。
7.13.1 固定的域
    如果域的宽度是固定的,但不是由域分隔符分割的。substr函数用来建立域。
    空域。 如果数据保存在固定宽度的域内,那么一些域就可能是空的。在下面的例子中,substr函数用来保存这些域,而无论它们是否含有数据。
    FIELDWIDTHS变量。 如果文件中域的宽度固定,则可以使用FILEDWIDTHS变量(仅在gawk中使用)。这个变量的值是以空格分隔的数字列表,列表中的每个数字都表示相应的域的宽度。如果设置了变量FIELDWIDTHS,FS的值就将被忽略。
    例如 # cat fixedfile
         abc1245556
         xxxyyyzzzz
         # awk 'BEGIN{FIELDWIDTHS="3 3 4"}{print $2}' fixedfile
         124
         yyy
    有$的数字、逗号和其他符号。在下面的例子中,价格域包含一个美元符号和一个逗号。要计算最后的结果,这些符号必须被去掉。可以用gsub处理这件事情。
7.13.2 捆绑和解捆绑文件
    捆绑程序。在Alfred、Brian Kernifghan和Peter Wienberger共同编写的《AWK Programming Language》中,捆绑文件的程序非常短。我们现在准备合并多个文件以节约磁盘空间,或者用电子邮件发送文件等等。
例如,
# awk '{print FILENAME, $0 }' file1 file2 file3 > bundled
    解捆绑。
例如,
# awk '$1 !=previous { close(previous); previous=$1}; \
    {print substr($0, index($0, "  ") 1) > $1}' bundled
7.13.3 多行记录
    目前例子中使用的数据文件,每个记录只占用一行。后面的数据文件,叫做checkbook,记录用空行来分隔,域用换行符来分隔。要处理这样的文件,应将记录分隔符RS赋值为空域分隔符FS赋值为换行符

$ cat a.txt
0010|1231412sdfaasd|sdfadfa

awk -F "|" 'gsub(/[1234567890]/,"",$2){print $1"|"$2"|"$3}' a.txt
0010|sdfaasd|sdfadfa