[TOC]
# 4.2 数据验证
> 数据验证也是Awk程序比较常用的功能之一,通过验证可以确保数据格式的合法有效性(至少是合理的)。这节包含一些检验输入数据有效性的小示例,通过了解这些示例,我们可以掌握更多Awk的用法。
## 示例: 每列数据类型检验
```
#!/usr/bin/awk -f
# colcheck.awk - 检验每列数据类型是数字还是字符串类型
# 输入: 数字或者字符串
# 输出: 不符合第一行格式类型的行数据
NR==1{
nfld = NF
for(i=1; i<= NF; i++)
type[i] = isnum($i)
} {
if(NF != nfld)
printf("第 %d 行有 %d 列数据而不是 %d 列\n", NR, NF, nfld)
for(i=1;i<=NF;i++)
if(isnum($i) != type[i])
printf("第 %d 行的第 %d 列值为: %s ,与第一行类型不符合\n", NR, i, $i)
}
function isnum(n){ return n~/^[+-]?[0-9]+$/}
```
验证结果:
```
输入数据:
1 a 1 a 1
1 1 1 1 1
输出结果:
第 2 行的第 2 列值为: 1 ,与第一行类型不符合
第 2 行的第 4 列值为: 1 ,与第一行类型不符合
```
## 示例:定界符的检验
> 有时候我们编写内容包含语句块(例如有开始标记`.START`和结束标记`.END`),但无法通过执行或者自动语法检查工具验证正确性时,我们就只能通过人工方式阅读验证,这非常低效,所以我们可以利用Awk来自动检验编写的内容是否有效。
```
/^\.START/{
if( p != 0)
printf("错误:在第 %d 行的.START 标记出现在了 .START 标记之后\n", NR);
p = 1
}
/^\.END/{
if( p == 0)
printf("错误:在第 %d 行的.END 标记前少了 .START 标记\n", NR);
p = 0
}
END{ if( p != 0) printf("错误:缺失 .END 标记" }
```
思考问题:
这个示例的定界符没有考虑定界符嵌套问题,那么如何扩展以下支持嵌套定界符的检验呢?
下面是匹配语句块语法检验的Awk程序,可以找到最近缺失匹配的位置:
```
#!/usr/bin/awk -f
# p12check.awk 定界符有效性检验
# 检验Awk的语句块{}定界符有效性(适用于C语言或Bash等语法检验)
/{/{
srow[p++] = NR
}
/}/{
if( p == 0 )
printf("语法错误:第%d行前缺少了{\n", NR)
else
erow[p--] = NR
}
END{
if( p != 0)
printf("语法错误:第%d行的}之后缺少与第%d行的{匹配\n", erow[p+1] , srow[p])
}
```
## 示例: 密码文件检验
> 对Linux/Unix系统熟悉的人都知道 `/etc/passwd`文件是保存用户的密码文件,存储了每个用户的名称、ID、组和登录Shell等信息。
passwd文件格式固定为冒号(:)分隔的7个字段。
```
用户名:加密的密码串:用户ID:组ID:全名:home目录:登录默认运行的程序
```
下面编写一段`pwdcheck.awk`检查`passwd`文件正确性脚本:
```
# pwdcheck.awk 检查passwd文件
BEGIN{ FS=":"}
NF != 7{ printf("第%d行不是7列数据:%s\n",NR,$0)}
$1~/[^A-Za-z0-9]/{ printf("第%d行用户名不是字母或数字组合:%s\n",NR,$0)}
$2 == ""{ printf("第%d行用户密码为空:%s\n",NR,$0)}
$3 ~/[^0-9]/{ printf("第%d行用户ID非数字:%s\n",NR,$0)}
$4 ~/[^0-9]/{ printf("第%d行组ID非数字:%s\n",NR,$0)}
$6 !~/^\//{ printf("第%d行用户HOME目录不是绝对路径:%s\n",NR,$0)}
```
这是一个密码文件检验的简单示例,当我们需要时可以继续修改或添加新的规则条件,每日执行检验保证密码文件的合法性。
## 示例:数据校验程序生成器
> 刚才密码文件检验程序是手工编写的,那么我们接下来看一个简单的自动生成Awk检验程序的示例。
```
BEGIN{ FS = "\t+" }
{ printf("%s {\n\tprintf(\"line %%d, %s: %%s\\n\", NR,$0)\n}\n",$1,$2)}
```
在终端执行结果:
```
## 执行方法
$ ./checkgen.awk rule.list
## 输入内容:
$ cat rule.list
NF != 7 不包含7列数据
$1 ~ /[^A-Za-z0-9]/ 用户ID非数字
$2 == "" 没有设置密码文件
##输出结果:
NF != 7 {
printf("line %d, 不包含7列数据: %s\n", NR,$0)
}
$1 ~ /[^A-Za-z0-9]/ {
printf("line %d, 用户ID非数字: %s\n", NR,$0)
}
$2 == "" {
printf("line %d, 没有设置密码文件: %s\n", NR,$0)
}
```
这个示例是一个抽象提取规则的示例,提取出模式串和提示信息串两部分作为输入数据,编写通用的生成检验脚本规则,这样可以达到编写一个脚本可以重复利用的效果,这样可以减少我们很多重复性劳动工作。
- 目录
- 概述
- 第一章 编写第一个Awk命令
- 1.1 什么是Awk命令
- 1.2 第一个Awk命令
- 第二章 Awk的模式匹配
- 2.1 Awk模式语法规则
- 2.2 Awk模式规则详解
- 第三章 Awk的动作规则
- 3.1 Awk动作匹配语法规则
- 3.2 Awk动作规则详解
- 第四章 Awk数据处理方法
- 4.1 数据转换和提炼
- 4.2 数据验证
- 4.3 数据打包与拆包处理
- 4.4 多行数据处理
- 4.5 随机数生成
- 第五章 Awk的输出报告和脚本封装
- 5.1 输出报告
- 5.2 封装查询结果和报告
- 第六章 Awk实现排序算法
- 6.1 插入排序算法实现
- 6.2 快速排序算法实现
- 6.3 堆排序算法实现
- 6.4 拓扑排序算法实现
- 总结