Shell脚本学习指南-第五章-管道的神奇魔力
第5章 管道的神奇魔力
5.1 从结构化文本文件提取数据
背景:/etc/passwd 用户密码文件
每行代表一个用户,冒号:作为字段分隔符,一共7个字段
|
|
- 用户名
- 加密密码占位
- UID 用户ID
- GID 用户组ID
- GECOS字段:用户姓名、办公室、电话(本例内部用
/再次分隔子字段) - 用户家目录
- 登录Shell
查看帮助:
man 5 passwd,5代表配置文件格式手册。
需求示例:从passwd生成办公室通讯录。
- 原始GECOS:
Adrian W. Jones/OSD211/555‑0123 - 需要拆分为:姓名、办公室编号、电话号码。
核心工具回顾
cut -d: -f5:按冒号分隔,提取第5字段awk -F: '{print $5}':冒号做分隔符,打印第5字段join:类似数据库join,把多个文件按照共同key(键)合并
前提:待合并文件必须以key排序;用
‑t:指定分隔符为冒号;-代表读取标准输入。
处理思路:
- 从passwd提取数据,生成3个临时文件,格式为
key:value,key为用户名pd.key.person.$$:用户名:格式化后的姓名pd.key.office.$$:用户名:办公室编号pd.key.telephone.$$:用户名:电话号码
$$:shell PID,用来生成唯一临时文件名。 - 使用
join多次合并这三份文件 cut删掉不再需要的key字段sort排序awk printf格式化输出,制表符\t分隔列
完整管道逻辑
例5‑1 办公室名录脚本关键点
trap信号处理:收到退出信号,执行rm删除临时文件。
设计思想:
- 脚本从标准输入读数据,输出到标准输出。支持管道重定向:
passwd‑to‑directory < /etc/passwd > out.txt- 后续想要增加字段(例如职位):只需要新增一个key文件,多加一条join,修改sort、awk格式化即可。
类比关系数据库:
- key=主键;
join等价SQL JOIN;文本文件相当于数据表。
5.2 结构化数据输出HTML网页
目标:把制表符分隔的通讯录文本,转换成HTML表格。
知识点:here‑document <<EOF
<<标记>>:here文档,shell读取直到标记行之前所有内容,交给cat作为标准输入。- 内部会做变量、命令替换。
HTML转义特殊字符
& < > 在HTML有特殊含义,必须替换成实体:
&→&<→<>→>
sed批量替换示例:
|
|
例5‑2 tsv‑to‑html脚本功能
输入:Tab分隔的文本;输出:HTML表格 流程:
- here‑document输出HTML头部样板
- sed完成特殊字符转义、把Tab替换成
</TD><TD>,每行包上<TR><TD>...</TD></TR> - here‑document输出HTML尾部样板
好处:不写死列数,任意Tab分隔文件都可以转HTML表格。
字符编码转换思路(例5‑3)
ISO‑8859‑1特殊字符(é å等)替换成HTML实体,思路就是一堆sed替换规则。实际项目一般交给专门工具处理。
5.3 文字解谜:利用系统单词词典
系统词典文件,每行一个英文单词:
/usr/share/dict/words、/usr/dict/words
例5‑4 puzzle‑help脚本:用正则在词典文件搜索单词
|
|
-h:不输出文件名-i:忽略大小写2> /dev/null:丢弃错误(文件不存在、无权限的报错)sort -u -f:去重,忽略大小写排序
示例:查找10位,第7位是x/z的单词
|
|
正则小提示:
[^aeiouy]方括号内部开头的^代表取反;和行首^不是同一个含义。
5.4 单词频率统计 wf脚本(经典McIlroy六步管道)
需求:统计文本里每个单词出现次数,输出频率最高的N个单词。
例5‑5 wf脚本完整拆解
tr -cs SET1 SET2-c:取反,不在SET1里面的字符-s:压缩连续重复字符
uniq -c:只对相邻重复行计数,所以前面必须sortsort -k1,1nr:n数字排序,r逆序从大到小${1:-25}参数展开:如果$1没有传入,默认值25
性能:大部分耗时消耗在
sort排序;其余步骤是线性处理。
使用示例:
|
|
5.5 taglist:提取XML/HTML标签内容
需求:解析xml,提取 <tag>内容</tag>,输出:计数、文本、标签名、文件名。
例5‑6 taglist脚本管道步骤
|
|
- sed:简化特定标签
- tr:把各类括号转为换行,打散文本
- egrep:只保留带闭合标签的行
- awk以
<>做分隔符,提取标签内文字、标签名称,附带文件名 - sort + uniq‑c:统计出现次数
- sort:按单词、标签排序
- awk记忆上一行单词,相同单词标记
<---提示,方便人工校对文档标签是否混用。
5.6 本章小结
- 结构化文本处理思路:使用简单分隔符(冒号、Tab),方便管道工具处理,输出继续作为下一个工具输入。
- 工具组合:
cut/awk提取字段;join做多文件按键合并;tr字符转换;sort排序;uniq统计去重;sed文本替换。 - 临时文件管理:使用
$$生成唯一名字,trap EXIT脚本退出自动清理,避免残留垃圾。 - here‑document
<<EOF:批量输出大段文本(HTML模板等)。 - 文本可以转换成HTML表格,Tab分隔格式是电子表格/脚本之间很好的交换格式。
- 复杂任务:不要写大程序;拆分成简单过滤器,管道串联,UNIX工具优先。
本章重点命令速查表
| 命令 | 作用 | 使用示例 |
|---|---|---|
cut -d分隔符 -f字段 |
按分隔符提取指定字段 | cut -d: -f1 /etc/passwd 按冒号分隔,提取第1列(所有用户名) |
join -t: file1 file2 |
按键合并两个已经排序的文件,类似数据库join | join -t: user.txt phone.txt 以冒号为分隔符,根据第一列key合并两个文件 |
tr -cs A‑Za‑z '\n' |
字符集替换、‑c取反、‑s压缩连续重复字符 |
tr -cs A-Za-z\' '\n' < test.txt 把所有非字母非撇号的字符替换成换行,每个单词一行 |
uniq -c |
统计相邻重复行出现次数;⚠前面必须先sort,否则统计不全 | sort words.txt uniq -c 排序之后统计每行重复出现多少次 |
sort -k1,1nr |
按指定字段数字逆序(从大到小)排序 | sort -k1,1nr -k2 file.txt 第1列按数字降序,第2列按文本升序 |
trap "rm xxx" EXIT |
脚本退出(正常/异常退出)时,自动执行清理命令 | trap "rm -f /tmp/tmp.$$" EXIT 脚本结束自动删除临时文件,避免残留垃圾 |
<<EOF |
here‑document,在脚本里输出一大段多行文本给命令(常见给cat) | cat <<EOF hello world EOF 输出两行文字 hello、world |
join:输入文件必须按key提前排序,否则结果错乱。uniq -c只识别挨在一起的重复行,分散的重复不会统计,所以管道里一定先sort再uniq -c。trap EXIT:不管脚本是正常结束,还是被Ctrl‑C中断,都会执行trap里面的清理命令。<<EOF:结束标记EOF顶格写,前面不能有空格;如果写成<<‑EOF,可以允许行首tab缩进。
文章作者 会写代码的小郎中
上次更新 2011-07-18
许可协议 CC BY-NC-ND 4.0