Shell脚本学习指南(四)——管道的神奇魔力

news2024/9/21 17:26:47

文章目录

  • 前言
  • 从结构化文本文件中提取数据
  • 针对Web的结构型数据
  • 文字解谜好帮手
  • 单词列表
  • 标签列表


前言

当你在UNIX里对付文字处理作业时,必须谨记一个UNIX工具使用原则就是:想清楚这个问题该如何划分为更简单的工作,每个部分是不是已有现成的工具能解决,还是你可以写几行Shell程序或使用脚本语言就能解决。

从结构化文本文件中提取数据

在UNIX下的管理性文件,大部分都是无须使用任何特殊的文件专用工具,即可编辑、打印与阅读的简易文本文件。这些文件大部分放在标准目录:/etc下。最常见的例子就是密码与组文件(passwd and group)、文件系统加载表(fstab或vsftab)、主机文件(hosts),以及默认的Shell启动文件(profile),以及系统启动与关机的Shell脚本(存放在子目录树rc0.d、rc1.d···rc6.d之下,也有可能是其他目录)。

/etc/passwd相关的结构信息

1用户名称
2加密的密码,或指出密码存储于另一个文件中
3用户ID数字
4用户组ID数字
5用户姓名,或其他相关数据(办公室号码、电话等)
6根目录
7登录的Shell

每个字段几乎对各种不同的UNIX程序都很重要,除第5个字段外。传统上,第5个字段用来置放用户相关的信息。其实原本叫做gecos子弹,这个名称有历史原因,它是在20世纪70年代在贝尔实验室时加入的,当初是为了我那个UNIX系统能与其他运行通用电子综合操作系统的计算机进行通信而产生的,后者需要UNIX用户相关的额外信息。今天,多数将它用来存放用户姓名,所以我们把这个字段简称为姓名字段。

范例,我们假定本地端点在姓名字段里存放其他信息:建筑物与办公室号码,以及电话号码,且这些数据与个人姓名以斜杠分隔。

#! /bin/bash
# 过滤/etc/passwd这类格式的输入流
# 并以此数据衍生出办公室名录
#
# 语法:
#        passwd-to-directory </etc/passwd > office-directory-file
#        ypcat passwd | passwd-to-directory > office-directory-file
#        niscat passwd.org_dir | passwd-to-directory > office-directory-file

umask 077


PERSON=/tmp/pd.key.person.$$
OFFICE=/tmp/pd.key.office.$$
TELEPHONE=/tmp/pd.key.telephone.$$
USER=/tmp/pd.key.user.$$


trap "exit 1"                                 HUP INT PIPE QUIT TERM
trap "rm -f $PERSON $OFFICE $TELEPHONE $USER" EXIT


awk -F: '{print $1 ":" $5}' > $USER


sed -e 's=/.*==' \
    -e 's=^\([^:]*\):\(.*\) \([^ ]*\)=\1:\3, \2=' < $USER | sort > $PERSON
sed -e 's=^\([^:]*\):[^/]*/\([^/]*\)/.*$=\1:\2' < $USER | sort > $OFFICE
sed -e 's=^\([^:]*\):[^/]*/[^/]*/\([^/]*\)=\1:\2' < $USER | dort > $TELEPHONE


join -t: $PERSON $OFFICE |
    join -t: - $TELEPHONE |
        cut -d: -f 2- |
            sort -t: -k1,1 -k2,2 -k3,3 |
                awk -F: '{printf("%-39s\t%s\t%s\t%s\n", $1, $2,%3) }'

在此程序里,重要的假设是在每条数据记录的一个唯一键值。有了这个唯一键值,数据的各种不同视图可以用成对的key:value方式维护在文件中。这里的键值为UNIX的用户名称,但在较大型的例子中,键值很可能是书目编号、信用卡号码、员工编号、国家退休体系编号。产品序号、学号等。现在你终于知道我们身上有多少编号了吧!有时在处理这些数据时需要的不一定是号码:只是需要具有唯一值的文本字符串。

针对Web的结构型数据

由于World Wide Web(WWW)广为流行,所以在前一节中开发办公司名录的形式,可以稍作修改,让数据以较漂亮的形式呈现。

Web文件多半都是由Hyper Text Markup Language(HTML)语言写成;它是Standard Generalized Markup Language(SGML)家族语言之一,而SGML自1986年起,陆续被定义在数个ISO标准中。

在这里插入图片描述

我们在这个小节,只需要小型的HTML子集,这部分我们将用一小段文字来介绍。如果你对HTML已熟悉,可以跳过这两页。

下面是我们写的一个遵循的小型HTML文件,是由我们其中一人所编写的一个好用工具所产生的Download

我没找到,有兴趣大家自己研究

使用方法

$ echo Hell, world. | html-pretty

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-13S68ek2-1669290014808)(file://C:\Users\g700382\AppData\Roaming\marktext\images\2022-11-22-11-32-08-image.png)]

(关于HTML的其他内容推荐大家上HTML 教程 | 菜鸟教程)

要将我们的办公司名录转换成正式的HTML,只需要再知道一件事:如何格式化表格,因为那才是真正的办公室名录,且我们不想使用打字机字体,强制每一行在浏览器显示时排列一致完全对齐。

因为我们选择保留办公室名录纯文本版里的特殊字段分割字符,所以有足够的信息可以识别每列中的单元格。而且,因为HTML文件里,空白多半不带特殊含义,我们就不需要特别主要标签是否完好排列。如果之后有需要,html-pretty还是可以做的很完美。我们转换过滤器有三个步骤:

  1. 输出前置的样板文件直到内文开始处。

  2. 将名录的每一行包括在表格标记里

  3. 输出结尾的样板文件

范例如下

#! /bin/bash
# 将制表符(Tab)所分割的文件,转换为遵循语法HTML
#
# 用法:
# tsv-to-html < infile > outfile

cat << EOFILE     #开头的样本文件(boilerplate)
<!DOCTYPE HTML PUBLIC "-//IEIF // DTD HTML//EN//3.0" >
<HTML>
    <HEAD>
        <TITLE>
            office directory
        </TITLE>
    <LINK REV="made" HREF="mailto:$USER@`hostname`">
    </HEAD>
    <BODY>
        <TABLE>
EOFILE


sed -e 's=&=\& amp;=g '\  #将特殊字符转换为实体(阅读时忽略中间的空格例 &)
    -e 's=<=\& lt;=g' \
    -e 's=>=\& gt;=g' \
    -e 's=\t=</TD><TD>=g' \ #提供表格标记
    -e 's=^.*S=          <TR><TD>&</TD></TR>='

cat << EOFILE        #结尾的样板文件
        </TABLE>
    </BODY>
<HTML>
EOFILE

<<标记为嵌入文件。后面会详细解释,指的就是Shell读取所有行,直到接在<<之后的定界符为止(本例中是EOFILE)、在被包含的行上执行变量与命令替换,以及将结果当成标准输入给命令。

文字解谜好帮手

字谜游戏会给你一些单词的线索,但大部分时候我们还是被困住,例如:具有10个字母的单词,以a,b起始,且第七个不是x就是z。

awk或grep进行正则表达式模式匹配是必须的,问题是:要查找什么文件呢?使用UNIX拼写字典是不错的选择,大部分系统的/usr/dict/words下都应该找得到它(还有像/usr/share/dict/words/usr/share/lib/dict/words也是可能出现的地方)。这是一个简答的文本文件,每行一个单词,以字典顺序排列。我们可以轻松地从任何的文本文件集合建立另一个具相似外表的文件,如下所示:

cat file(s) | tr A-Z a-z | tr -c a-z\' '\n' | sort -u

第二个管道步骤是将大写字母转换为小写,第三个则是以换行字符取代非字母字符,最后为结果进行排序,并去除重复部分,让每行都为唯一值。在第三步里,视撇号(')为字母,因为他们在缩写里会用到。每个UNIX系统具有可以此方式处理的整租文字——例如格式化后的手册页在/usr/man/cat*/*与/usr/local/man/cat*/*内。我们的系统里就有一个提供了一百万行以上的文本,并且产生了44 000个左右的唯一单词。在internet上你也可以找到很多种语言的单词列表。

我们假设已经以此方式建立了单词列表的集合,并将它们存储在一个标准的地方,以便我们的脚本可以找到参考它。编写如下程序

#! /bin/bash
# 通过一堆单词列表,进行类似egrep的模式匹配
# word lists
#
# 语法
#    puzzle-help egrep-pattern [word-list-files]

FILES="
        /usr/dict/words
        /usr/share/dict/words
        /usr/share/lib/dict/words
        /usr/local/share/dict/words.biology
        /usr/local/share/dict/words.chemistry
        /usr/local/share/dict/words.general
        /usr/local/share/dict/words.knuth
        /usr/local/share/dict/words.latin
        /usr/local/share/dict/words.manpages
        /usr/local/share/dict/words.mathematics
        /usr/local/share/dict/words.physics
        /usr/local/share/dict/words.roget
        /usr/local/share/dict/words.sciences
        /usr/local/share/dict/words.UNIX
        /usr/local/share/dict/words.webster
      " 
pattern="$1"

egrep -h -i "$pattern" $FILES 2> /dev/null |sort -u -f  | fmt

FILES变量保存了单词列表文件的内建列表,可供各个本地站点定制。grep-h选项指示最后结果不要显示出文件名,-i选项为忽略字母大小写,我们还用了 2> /dev/null丢弃标准错误信息的输出,这是单词列表文件不存在或是在他们缺乏必须的读取权限的情况。最后的sort步骤则可以简化最后的结果,让列表没有重复单词,并忽略字母大小写,fmt重新编排输出,方便阅读

测试如下,检查以b开头第七位为x或z的10位字母

在这里插入图片描述

单词列表

这里有个故事大家有兴趣可以阅读

在这里插入图片描述

总之就是让你将一个文件中所用的单词频率列出来,针对这种问题使用UNIX工具来处理会比直接编程要简单的多。

接原文:

将复杂的问题切分成数个较简单的部分,简单到你已经知道这个部分该怎么处理。为解决单词出现频率问题,Mcllroy将纯文本文件转换为单词列表,一行一个字(由tr来完成此工作)、将单词对应到单一的字母大小写(一样还是使用tr)、单词列表的排序(用sort)、从单词列表以计数的数字由大到小排序,最后,显示单词列表的前几项(这里使用sed,不过head也可以)。

最后形成的程序应该值得给他一个名称wf(word frequency,单词出现的频率之意),然后附上注释标题,打包成Shell脚本。我们也扩充了Macllroy原本的sed命令,让输出列表长度参数可选,并现代化sort选项

#! /bin/bash
# 从标准输入读取文本流,再输出出现频率最高的前n(默认值:25)个单词的列表
# 附上出现频率的计数,按照这个计数由大到小排列
# 输出到标准输出
#
# 语法:
#   wf [n]


tr -cs A-Za-z\' '\n' |   #将非字母字符置换成换行符号
    tr A-Z a-z |         #所有大写字母转换成小写
        sort |           # 由小到大进行排序单词
            uniq -c |    # 去除重复,并显示计数
                sort -k1,1nr -k2 | #计数由大到小排序后,再按照单词由小到大排序
                    sed ${1:-25}q  #显示前n行,默认25

测试:默认25

在这里插入图片描述

pr用于重新格式化输出结果,以每行4列显示。(这里不对该问题的复杂度进行分析,有兴趣的可自己研究)。

测试,前10行

在这里插入图片描述

测试前1000

在这里插入图片描述

还可以通过tail显示后面不常用的,grep -c '^ *1.'只出现1次的单词大家自己尝试

出现任意次次的也可以使用awk来实现

./wf.sh 1000 < /etc/passwd | awk '$1 >=5' | wc -l

在这里插入图片描述

标签列表

tr命令可用于取得单词列表,但其更常见的用法是:将一组字符集转换成另一组。这也导致了一个问题:如何确保整篇5万行左右的原稿文件具有一致的标记(markup)呢?例如,当我们在正文中提到一条命令时,可将命令标记为<command>tr</command>,但是在其他地方,我们可能举例说明你输入的内容,便会使用<literal>tr</literal>这样的标记。还有一种是提及手册参考时,标记形式为<emphasis>tr</emphasis>

后面范例缩写的程序就是这类问题的解决方案。该程序找出写在同一行里开始/结束的一对标签(tag),然后再输出一个排序列表,该列表将标签的使用与输入文件相关联。此外,对于多次的方式标记相同单词的地方,给出一个箭头标志。下列片段即为应用该程序后的输出

在这里插入图片描述

列出标签的任务想当然是很复杂的,如果你以最传统的程序语言来做也有点困难,即使拥有很大规模的类库。但是你使用UNIX管道,搭配你已熟悉的几个工具,只要9个步骤就能完成。

单词出现频率计算程序无法处理多个命名的文件:它假设仅有单一流。不过这也不是太严重的限制,因为我们可以很简单地通过cat将多个输入文件喂给它。不过在这里,我们需要有文件名,因为知道出问题但不晓得问题出在哪里,这对我们是没有好处的。所以,文件名成了程序的单个参数,在脚本中可通过$number来取得。

  1. 通过cat将输入文件给管道。当然,也可以省掉这个步骤,只需从$1中重定向下一步的输入,不过我们觉得在一个复杂的管道里”将数据生成“与”数据处理“分开,会比较有条例,而这么做在程序日后需要在另一个步骤插入新的管道时,也会容易些。cat "$1" | ...
  2. 通过sed简化Web URL所需的另一种复杂标记“
  3. ...|sed -e 's#systemitem *role="#URL#g"' -e 's#systemitem#/URL#'| ...这是将标签,如<systemitem role="URL">与</systemitem>分别转换成较简单的<URL>与</URL>标签。
  4. 下一个步骤使用tr将空白与成对的定界符转换为换行符:...|tr '(){}' '\n\n\n\n\n\n\n' | ...
  5. 至此,输入数据包括一行一个单词。这里所指的单词,不是真正的文本就是SGML/XML标签。所以我们下一步骤我们使用egrep,选定由标签括起来的单词...|egrep '>[^<>]+</' | ...这个正则表达式会匹配由标签括起来的单词:右尖括号,接着至少一个非尖括号字符,跟着一个左尖括号,再接上一个斜杠(也就是结束标签)。
  6. 至此,输入数据包括了带有标签的行,第一个awk步骤使用尖括号作为分割字符,所以当输入为<literal>tr</literal>,即切分为4栏,依次是:一个空字段、literal、tr,最后为/literal。文件名通过命令行传给awk,其中-v选项把awk变量FIEL设置为此文件名。该变量之后会应用到print语句上,以输出单词、标签与文件名:...|awk -F '[<>]' -v FILE="$1" '{printf ("%-31s\t%-15s\t%s\n", $3, $2,FILE)}' | ...
  7. sort步骤是以单词顺序排列每一行:...| sort |...
  8. uniq命令提供初始化的计数字段。输出为记录列表,其中字段一次为计数,单词,标签,文件:...|uniq -c |...
  9. 第二个sort是将输出结果以单词及标签的顺序排列(第二,三字段)...|sort -k2,2 -k3,3 | ...
  10. 最后步骤是使用小小的awk程序,过滤掉连续的行,加上结尾的箭头符号,当出现与上一行相同单词时使用。然后,此箭头符号可清楚地指出那个字使用了不同的标记,也就是作者、编辑或出版社相关人员应特别检查的地方:...|awk '{print ($2 == Last) ? ($0 "<-----"):$0 Last = $2}'

完整的程序如下

#! /bin/bash 
# 读取命令行上给定的HTML/SGML/XML文件
# 找出包含像<tag>word</tag>这样的标记,在输出到标准输出
# 该标准输出将以制表符(tab)分割字符,依次为
#
# 计数    单词    标签    文件名
# 按照单词与标签由小至大排序。
#
# 语法:
# taglist xml-file
cat "$1" |
    sed -e 's#systemitem *role="url"#URL#g' -e 's#/systemitem#/URL#' |
        tr ' (){}[]' '\n\n\n\n\n\n\n' |
          egrep '>[^<>]+</'  |
            awk -F '[<>]' -v FILE='$1' \
                '{printf("%-31s\t%-15s\t%s\n", $3, $2, FILE)}' |
                sort |
                    uniq -c |
                        sort -k2,2 -k3,3 |
                            awk '{
                                    print ($2 == Last) ? ($0 " <----"): $0
                                            Last = $2
                                 }'

后面会分析如何将标签列表应用到多文件的情况上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/32911.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Servlet上传文件

一、核心方法 1、HttpServletRequest类相关方法 方法描述Part getPart(String name)获取请求中给定name的文件Collection<Part> getParts()获取所有的文件 2、Part类相关方法 方法描述String getSubmittedFileName()获取文件名String getContentType()获取文件类型…

【Hack The Box】Linux练习-- Knife

HTB 学习笔记 【Hack The Box】Linux练习-- Knife &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月17日&#x1f334; &#x1f36…

Docker——镜像操作命令

目录 一、镜像操作命令 1.1 镜像操作命令&#xff08;常用图示&#xff09; 1.2 查看docker帮助文档 1.3 案例&#xff1a;从DockerHub拉取Nginx镜像并查看 1.3.1 拉取 1.3.2 查看 1.4 案例&#xff1a;利用docker save将Nginx镜像导出磁盘&#xff0c;然后再通过load加…

一文讲清场景工程方法论及运维组织能力内化

随着业务支撑要求的变化&#xff0c;和技术架构的升级&#xff0c;运维管理建设模式跟之前也有了很大区别&#xff0c;场景上更注重主动工作、防范风险&#xff0c;而体系上更注重赋能组织、平台化、一体化。 因而&#xff0c;我们基于过去几年的大量客户实践&#xff0c;分享…

关于HashMap默认容量的选择

HashMap默认容量思量什么是容量容量与哈希hash的实现指定容量初始化扩容思考总结集合是Java开发日常开发中经常会使用到的&#xff0c;而作为一种典型的K-V结构的数据结构&#xff0c;HashMap对于Java开发者一定不陌生。 在日常开发中&#xff0c;经常会像如下方式创建一个Has…

使用IntelliJ IDEA创建Maven项目并上传项目至gitlab等远程仓库

步骤&#xff1a; 新建Maven项目&#xff0c;添加web.xml文件&#xff0c;只编写一个html页面用于测试&#xff0c;tomcat部署&#xff0c;配置git&#xff0c;上传到gitlab 1. Maven创建Web项目&#xff0c;命名为testci 2. 点击File —> Project Structure —>Facets—…

分享35个ASP源码,总有一款适合您

链接&#xff1a;https://pan.baidu.com/s/1t-Biw9LihpPwrwfJNLoTPw?pwdfxol 提取码&#xff1a;fxol 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c;大家下载后可以看到。 40400互联网上报名系统 医院会员注册系统…

132-142-Hadoop-Yarn-常用命令生产环境等配置

132-Hadoop-Yarn-常用命令生产环境等配置&#xff1a; Yarn常用命令&#xff1a; 1、启动集群 2、执行一个案例 [roothadoop102 hadoop-3.1.4]# hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.4.jar wordcount /input /ouput013、查看结果 yarn applic…

【云原生 | Kubernetes 实战】02、k8s 核心资源 Pod 介绍

目录 一、Pod是什么&#xff1f; 白话解释&#xff1a; 1.1 Pod如何管理多个容器&#xff1f; 1.1.2 Pod网络 1.1.3 Pod存储 1.2 之前学习过容器&#xff08;如docker&#xff09;&#xff0c;为什么还需要Pod&#xff1f; 1.2.1 代码自动发版更新 1.2.2 收集业务日志 …

【Kubernetes系列】工作负载资源之ReplicaSet

文章目录概述工作原理何时使用示例ReplicaSet的配置Pod 模板Pod 选择算符Replicas操作使用ReplicaSet删除 ReplicaSet 和它的 Pod只删除 ReplicaSet将 Pod 从 ReplicaSet 中隔离扩缩 ReplicaSetReplicaSet 的替代方案Deployment&#xff08;推荐&#xff09;裸 PodJobDaemonSet…

twitter推文采集案例

案例内容:twitter的推文采集。 通过游客身份获取twitter中用户的推文。 用户推文:只能翻22页,1页45条左右。 每个用户最多获取最近900条推文 (登录后),不登录区别也不大。 UserByScreenName 一般只有用户名的时候,需要先通过UserByScreenName接口获取 rest_id 用于后…

frp穿透你的远程桌面

缘起 作为一个程序员&#xff0c;经常会遇到需要使用远程桌面的述求&#xff08;居家办公、加班&#xff0c;你懂的&#xff09;。所以&#xff0c;在网上找一圈远程桌面解决方案之后&#xff0c;最终还是使用frp来穿透远程桌面。&#xff08;推荐使用&#xff09; 前提 需要…

计算机内功修炼:程序的机器级表示(C与汇编)

程序的机器级表示历史观点程序编码1. 机器级代码2. 代码示例数据格式访问信息1. 操作数指示符2. 数据传送指令算术与逻辑操作1. 加载有效地址2. 一元操作和二元操作3. 移位操作例子特殊的算术操作控制1. 条件码2. 访问条件码3. 跳转指令及编码4. 翻译条件分支5. 循环6. 条件传送…

[附源码]计算机毕业设计JAVA龙虎时代健身房管理系统

[附源码]计算机毕业设计JAVA龙虎时代健身房管理系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM …

JDBC数据库的连接

JDBC数据库的连接 简介&#xff1a;本文以简洁的篇幅带领大家快速入门java连接数据库&#xff0c;以查询为演示方案。 编写代码步骤 创建工程&#xff0c;导入驱动jar包 下载官网&#xff1a;https://downloads.mysql.com/archives/c-j/ 注册驱动 # mysql5的注册驱动的办法…

BEV感知PETR-V1和PETR-V2

参考代码&#xff1a;PETR 1. 概述 介绍&#xff1a;这两篇文章提出了以位置编码转换&#xff08;PETR&#xff0c;position embedding transformation&#xff09;为基础的BEV感知方法&#xff0c;按照方法中组件不同可将PETR划分为V1和V2版本。在V1版本中提出了基础版本的PE…

【C++】c++11学习-常用特性总结

前言 由于种种历史原因&#xff0c;C的C11版本更新带来了很多有用的东西~&#xff0c;在C98的基础语法体系之上&#xff0c;来看看C11新增了哪些实用的特性吧~ &#xff08;加把劲~~(๑╹◡╹)&#xff89;"""&#xff09; 目录 一、列表初始化 1.原始的列…

Mysql8.x版本主从加读写分离(二) mysql8.x读写分离

Mysql8.x版本主从加读写分离&#xff08;一&#xff09; mysql8.x主从_争取不加班&#xff01;的博客-CSDN博客 Mycata需要使用jdk 单独一台服务器部署的mycat 192.168.11.143 手动上传jdk的包 tar zxvf jdk-8u121-linux-x64.tar.gz -C /usr/local/ 解压 cd /usr/local…

c++ - 第14节 - c++中的多态

1. 多态的概念 多态的概念&#xff1a;通俗来说&#xff0c;就是多种形态&#xff0c;具体点就是去完成某个行为&#xff0c;当不同的对象去完成时会产生出不同的状态。举个栗子&#xff1a;比如买票这个行为&#xff0c;当普通人买票时&#xff0c;是全价买票&#xff1b;学生…

如何运行黑马程序员redis项目黑马点评(hm-dianping)、常见报错解决与部分接口的测试方法

文章目录一、相关链接二、下载代码方法一&#xff1a;使用git clone方法二&#xff1a;直接下载程序zip压缩包三、如何运行这份代码运行sql文件1、先新建数据库hmdp2、导入项目中的hmdp.sql文件修改application.yaml配置文件配置Mysql1、配置驱动2、配置url&#xff08;这个不一…