郑州建网站公司,长春网站建设长春,网站域名后缀cc,道滘镇网站建设2.6 个人库 
Awk提供了适量的内置函数库#xff0c;如 length、sub、substr、printf 等其他十来个#xff1b;在A.2.1节的参考手册中都有列出。你可以自己创建更多函数#xff0c;以便有需要时引入到Awk程序中。比如内置库函数 sub 和 gsub 都只能返回替换的次数#xff0c…2.6 个人库 
Awk提供了适量的内置函数库如 length、sub、substr、printf 等其他十来个在A.2.1节的参考手册中都有列出。你可以自己创建更多函数以便有需要时引入到Awk程序中。比如内置库函数 sub 和 gsub 都只能返回替换的次数而你可以用它们来写自己的字符串替换函数把返回改成替换后的字符串。本节还会给出少量样例都是这些年来我们认为很有用的。 
函数 rest(n) 返回从第n个域开始的所有输入域。 
# rest(n): 返回一个字符串包含 第n到第NF个域用空格分隔function rest(n,   s) {s  while (n  NF)s  s $n  return substr(s, 1, length(s)-1)  # 删除末尾空格
}# 测试下:
{ for (i  0; i NF1; i)printf(%3d [%s]\n, i, rest(i))
}注意由于$符号的优先级比高s  s $n    这一行要改为 s  s $(n)   才能正常运行否则会陷入死循环。原书代码有问题。 
函数 rest 有个局部变量 s。Awk中没有变量声明因此只要是调用者没有提供的参数都会被当作函数的局部变量很遗憾这是语言设计的糟糕之处。在本例中调用 rest 的时候只带了一个参数 n 所以第二个参数 s就是函数内的局部变量。 
按惯例我们在写函数声明的时候都会在局部变量名前面多加些空格这样可以把参数和局部变量区分清楚。还有一种做法是使用容易区分的名字比如用下划线做前缀或后缀 
function rest(n,   _s) {_s  while (n  NF)_s  _s $(n)  return substr(_s, 1, length(_s)-1)
}不过这样看起来会有点难受。 
当然还有一种做法是在所有局部变量前面加上一个不用的参数比如 locals 或者下划线_。上面这三种方法都是针对糟糕的语言设计做出的不完美的变通。 
你还可以自己写一些 rest 的变种函数比如写个 subfields(m, n) 返回从第m 到 第n 的连续域的序列或者写个 join 把一个数组中的所有值变成空格分隔的序列或者把一个数组转换成JSON对象 
{name: value, ...} 
如果你使用标准Awk那就不得不手动拷贝这些函数到你的程序里面这实际就是复制粘贴很简单但是有风险。A.5.4节的参考手册里有个 include 程序。或者可以使用多个 -f 参数来包含多个Awk 源文件。 
日期格式化 
本章前面2.5节的例子里使用的日期格式为 mm/dd/yy这是美国的使用习惯但与其他地方不一样而且很难对这种格式做排序或其他数学计算。我们能很轻松写出一个 datefix 函数来把这种格式转换成ISO标准格式 yyyy-mm-dd这样的话数据就能直接用日期来排序了。 
# datefix: 把 mm/dd/yy 转换成 yyyy-mm-dd (从1940年到2039年)awk 
function datefix(s,   y, date) {split(s, date, /)y  date[3]40 ? 2000date[3] : 1900date[3]  # 任意年份return sprintf(%4d-%02d-%02d, y, date[1], date[2])
}{ print(datefix($0)) }$*$ datefix
12/25/23
2023-12-25Awk内置函数 split (s, arr, sep) 用分隔符 sep 将字符串 s 分割到数组 arr 中。元素编号从1开始split 返回元素的个数。分隔符是正则表达式可以写成字符串形式如 sep 或是用斜杠包围如 /sep/。如果没有 sep 参数且传给Awk程序的参数里包含了 --csv则字符串以CSV格式进行分割否则就使用域分隔符变量 FS见A.5.2节 来进行分割。有一种特殊情况如果 sep 是空字符串  或空正则表达式 //则字符串会被分割成单个字符即每个数组元素一个字符。 
上面的代码用了一个比较随意的规则来将两位数的年份转换成四位数的如果小于40则认为是20xx年否则认为是19xx年。 
运算符 ?: 的语法为 表达式1 ? 表达式2 : 表达式3与C语言一样。它对表达式1求值如果为真则结果为表达式2否则为表达式3只会对表达式2和3中的一个求值。实际上 ?: 是能用在表达式里面的 if-else 的紧凑写法。它非常方便但很容易被滥用造成代码难以读懂。 
最后注意 sprintf 中的转换%02d 用两位宽度来打印整数位数不足时在前面补0。 
假如我们想要从本地操作系统中获取当前的日期和时间我们可以使用Unix命令 date然后将它的返回内容重新格式化。最简单的方法是在Awk里运行 date并将它的输出管道化给Awk的 getline 函数这个函数从文件或管道中读取输入 
date | getline date    # 获取当前日期和时间
split(date, d, / /)      # 用字符串   也可以
date  d[2]   d[3] ,  d[6] 
只要一点点处理就能将这种日期格式 
Wed Jul 12 07:16:19 EDT 2023 
转换成下面的格式注意如果日期是一位数d[3]会是空字符d[4]才是日期年份是[7] 
Jul 12, 2023 
也可以转换成你想要的任意格式可能会用上前面的 datefix 函数。 
getline 命令和管道的更多细节参见 A.5.4。 
假定你要把月份转换成数字Jan是1Feb是2等等。可以使用一系列赋值语句来做比如m[Jan]1, m[Feb]2, 以此类推但这样写起来太繁琐了。一个不错的替代方案是写个函数把字符串拆到索引数组中如下 
# isplit - 用str构造索引数组function isplit(str, arr,   n, i, temp) {n  split(str, temp)for (i  1; i  n; i)arr[temp[i]]  ireturn n
} isplit 函数很像 split区别是它构造出来的数组下标是字符串里面的单词而对应的值是单词在字符串中的索引。执行如下语句后 
isplit(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec, m) 
m[Jan] 的值是1而m[Dec]的值是12。 
前面说过split函数可以有第三个参数是一个正则表达式。你可以对 isplit 做扩展以字符串方式对 isplit 函数传入这个参数。 
练习2-5、写个tomorrow脚本以合适的格式打印出明天的日期。 
练习2-6、写出返回修改后的字符串的 sub 和 gsub 版本类似Python的 re.sub函数。 2.7 小结 
我们在本章展示了一些个人认为有用的脚本。很可能大部分都不是读者直接想要的但我们希望它们能在你自己写程序时带来一些启发而且这些样例中展示了不少技巧会让你编程更轻松。 
本章的例子大部分是基于如下内容的组合计算相关数值的算术表达式储存信息的数组以及封装计算的函数。这些机制是编程中至关重要的。在Awk里使用它们特别容易因为Awk就是围绕它们来设计的但同样的方法在其他语言里也是无价的非常值得你花时间来掌握。 第二章完