sed awk 第二版学习(三)—— 编写 sed 脚本

2024-09-05 09:36
文章标签 学习 编写 脚本 第二 awk sed

本文主要是介绍sed awk 第二版学习(三)—— 编写 sed 脚本,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、在脚本中应用命令

二、寻址上的全局透明

三、测试并保存输出

1. 用于测试 sed 的 shell 脚本 testsed

2. sed 永久性改动的 shell 脚本 runsed

四、sed 脚本的四种典型应用

1. 对同一文件的多重编辑

2. 改变一组文件

3. 提取文件内容

(1)提取宏定义脚本 getmac

(2)生成提纲的脚本 do.outline

4. 编辑工作转移

五、几个使用 sed 脚本的提示


        使用 sed 可以将类似于 vi 编辑器中手动的操作过程提取出来,并转换成一个非手动的过程,即通过执行一个脚本来实现。大多数不熟悉 sed 的人都觉得,编写执行一系列编辑动作的脚本,比手动做一些改动更冒险。这种担心的原因是自动化任务会发生一些不可逆的事情。学习 sed 的目标就是要理解它从而可以预测执行结果。

        这就要求采用可控制的方式来使用 sed。在编写脚本时,应遵循以下这些步骤:

  1. 在着手做之前要弄清楚想做什么。
  2. 明确地描述处理过程。
  3. 在提交最终的改变之前反复测试这个过程。

        检测脚本是否中工作的最好方式,是使用不同的输入样本进行测试并观察结果。sed 工作的三个基本原理为:

  • 脚本中的所有编辑命令都将依次应用于每个输入行。
  • 命令应用于所有的行(全局的),除非行寻址限制了受编辑命令影响的行。
  • 原始的输入文件未被改变,编辑命令修改原始行的备份并将修改后的备份发送到标准输出。

一、在脚本中应用命令

        一次一行的设计的一个优点是 sed 比交互式屏幕编辑程序更适合处理大文件。后者必须将整个文件(或大部分)读入内存,这可能产生内存溢出或处理大文件时速度非常慢。sed 首先将整个编辑脚本应用于第一个输入行,然后再读取第二个输入行并对其应用整个脚本。因为 sed 总是处理原始行的最新形式,所以生成的任何编辑工作都会改变后续命令的应用的行。sed 不会保留最初的行,这意味着与原始输入行匹配的模式可能不再与经过编辑操作之后的行匹配。

        sed 维护一种模式空间,即一个工作区或临时缓冲区,当应用编辑命令时将在那里存储单个输入行。当应用了所有的指令后,当前行被输出并且输入的下一行被读入模式空间。然后脚本中的所有命令应用于新读入的行。

        结果是,任何 sed 命令都可以为应用下一个命令改变模式空间的内容。模式空间的内容是动态的,而且并不总是匹配最初的输入行。看个例子,假设输入为 pig cow,希望的输出为 cow horse。下面的 sed 命令输出中包含两个 horse:

s/pig/cow/
s/cow/horse/

        第一个命令将“pig”换成“cow”,第二个命令在同一行上将“cow”换成“horse”时,它还改变了由“pig”换成的“cow”。这个错误只是脚本命令中的顺序问题,技巧在于反转命令的顺序:

s/cow/horse/
s/pig/cow/

        一些 sed 命令会改变整个脚本的流程,例如 N 命令将另一行读入模式空间但不删除当前行,所以可以用来测试跨越多行的模式。其它一些命令告诉 sed,在到达脚本底部之前退出或者转到带标记的命令。sed 还维护了称为保持空间(hold space)的令一个临时缓冲区。可以将模式空间的内容复制到保持空间并在以后检索它们。

二、寻址上的全局透明

        sed 是隐式全局的,即缺省将命令应用于每个输入行。行地址用于提供操作(或限制)的上下文环境。sed 命令可以指定零个、一个或两个地址。每个地址都是一个描述模式、行号或者行寻址符号的正则表达式。

  • 如果没有指定地址,那么命令将应用于每一行。
  • 如果只有一个地址,那么命令应用于与这个地址匹配的任意行。
  • 如果指定了由逗号分隔的两个地址,那么命令应用于匹配第一个地址的第一行和它后面的行,直到匹配第二个地址的行(包括此行)。
  • 如果地址后面跟有感叹号(!),那么命令就应用于不匹配该地址的所有的行。

        删除所有行:

d

        只删除第一行:

1d

        行号指由 sed 维护的内部行号,该计数器不会因为多个输入文件而重置。因此不管指定多少个输入文件,在输入流中也只有一行 1。同样输入流也只有一个最后的行,可以使用寻址符号 $ 指定。

        删除输入的最后一行:

$d

        当正则表达式作为地址提供时,命令只影响于这个模式匹配的行。正则表达式必须封闭在斜杠(/)中。

        删除空行:

/^$/d

        如果提供两个地址,那么就指定了命令执行的行范围。

        删除 .TS 开头的行,一直删到(包含).TE 开头的行:

/^\.TS/,/^\.TE/d

        删除从行 50 到最后一行的所有行:

50,$d

        可以混合使用行地址和模式地址。

        删除从第一行直到第一个空行的所有行:

1,/^$/d

        可以把第一个地址看做是启动动作,并把第二个地址看做是禁用动作。sed 没办法先行决定第二个地址是否会匹配。一旦匹配了第一个地址,这个动作就将应用于这些行,于是命令应用于所有随后的行直到第二个地址被匹配。上例中如果没有空行,那么将删除所有行。

        跟在地址后面的感叹号会反转匹配的意义。

        删除除了 .TS 到 .TE(实际上是提取 .TS 到 .TE) 开头的行:

/^\.TS/,/^\.TE/!d

        sed 使用大括号({})将一个地址嵌套在另一个地址中,或者在相同的地址上应用多个命令。如果想指定行的范围,然后在这个范围内指定另一个地址,则可以嵌套地址。

        删除 .TS 开头到(包含).TE 开头的行中的空行:

/^\.TS/,/^\.TE/{
/^$/d
}

        左大括号必须在行末,而且右大括号必须独占一行。要确保在大括号后没有空格。可以使用大括号将编辑命令括起来以对某个范围的行应用多个命令。

        不仅删除 .TS/.TE 块中的空行,还在块中执行两个替换:

/^\.TS/,/^\.TE/{
/^$/d
s/^\.ps 10/.ps 8/
s/^\.vs 12/.vs 10/
}

三、测试并保存输出

        缺省 sed 将所有行送往标准输出(一般是屏幕),包括被修改的行和没有被修改的行,可以用重定向将这些输出保存到一个新文件。不要将输出重定向到输入文件,否则会改写输入文件,甚至可能在 sed 处理这个文件之前发生,并破坏数据。

# 将 sed 输出重定向到新文件
$ sed -f sedscr testfile > newfile# 比较文件,验证结果
$ diff testfile newfile

1. 用于测试 sed 的 shell 脚本 testsed

$ cat testsed 
#!/bin/bashfor x
dosed -f sedscr $x > tmp.$xdiff $x tmp.$x
done

2. sed 永久性改动的 shell 脚本 runsed

$ cat runsed 
#! /bin/bashfor x
doecho "editing $x: \c"if test "$x" = sedscr; thenecho "not editing sedscript!"elif test -s $x; thensed -f sedscr $x > /tmp/$x$$if test -s /tmp/$x$$thenif cmp -s $x /tmp/$x$$thenecho "file not changed: \c"elsemv $x $x.bak # save original, just in casecp /tmp/$x$$ $xfiecho "done"elseecho "Sed produced an empty file\c"echo " - check your sedscript."firm -f /tmp/$x$$elseecho "original file is empty."fi
done
echo "all done"

四、sed 脚本的四种典型应用

1. 对同一文件的多重编辑

        推荐逐步编写的技术,因为将每个命令隔离开可以容易看出哪些功能实现了,哪些还没有。如果同时尝试几个命令,则在问题出现时需要按和创建命令相反的过程来结束,即一个一个地删除命令直到找到问题为止。来看下面的例子。

        需求:

  1. 用 .LP 取代所有空行。
  2. 删除每行所有的前导空格。
  3. 删除打印机下划线的行,即以“+”开始的行。
  4. 删除添加在两个单词之间的多个空格。

        用部分文本逐个测试 sed 命令脚本:

$ cat sedscr
s/^ *$/.LP/
/^+ */d
s/^ *//
s/ */ /g
s/\. */. /g$ sed -f sedscr horsefeathers

        下一阶段使用 testsed 在完整的文件上测试脚本并彻底地检查结果,当对这个结果满意时,可以使用 runsed 生成永久性的改变:

$ runsed hf.product.bulletin
all done

2. 改变一组文件

        sed 最常见的用法是对一组文件进行一系列搜索和替换的编辑操作。这样的脚本不需要有趣,只要它们有用并能节省手工工作就行。通常这些脚本只是将单词或短语变成另一种形式的替换命令列表,例如:

s/ON switch/START switch/g
s/ON button/START switch/g
s/STANDBY switch/STOP switch/g
s/STANDBY button/STOP switch/g
s/STANDBY/STOP/g
s/[cC]abinet [Ll]ight/control panel light/g
s/core system diskettes/core system tape/g
s/TERM=542[05] /TERM=PT200 /g
s/Teletype 542[05]/BigOne PT200/g
s/542[05] terminal/PT200 terminal/g
s/Documentation Road Map/Documentation Directory/g
s/Owner\/Operator Guide/Installation and Operation Guide/g
s/AT&T 3B20 [cC]omputer/BigOne XL Computer/g
s/AT&T 3B2 [cC]omputer/BigOne XL Computer/g
s/3B2 [cC]omputer/BigOne XL Computer/g
s/3B2/BigOne XL Computer/g

        一旦这个脚本通过测试,就可以使用 runsed 一次性处理多个文件。文本之间有很大的不同,不能认为一种特殊情况为真,所有情况就都为真。测试每个文件是不切实际的,因此选择有代表性且包含异常的测试文件非常重要。使用 grep 检查大量输入很有帮助。

        在某些方面,编写脚本就像为给定事实的某种集合设计一个假设。通过增加测试数据来试着验证假设的合法性。如果打算在多个文件上运行该脚本,使用 testsed 首先在较小的示例上测试它,然后在许多文件上运行这个脚本。接着比较临时文件和原始文件来确认假设是否正确,有问题时修改脚本。花费在测试上的时间越多,那么在解决由拙劣脚本导致的问题上花费的时间就越少。

3. 提取文件内容

        sed 应用程序的一种典型的用法是从文件中提取相关的材料,这一功能类似于 grep,而且它具有在输出之前修改输入的又一优点。

(1)提取宏定义脚本 getmac

        脚本内容:

#! /bin/bash
# getmac -- print mm macro definition for $1
sed -n "/^\.de$1/,/^\.\.$/p" /usr/lib/macros/mmt

        执行方式:

$ getmac BL
.deBL
.if\\n(.$<1 .)L \\n(Pin 0 1n 0 \\*(BU
.if\\n(.$=1 .LB 0\\$1 0 1 0 \\*(BU
.if\\n(.$>1 \{.ie !\w^G\\$1^G .)L \\n(Pin 0 1n 0 \\*(BU 0 1
.el.LB 0\\$1 0 1 0 \\*(BU 0 1 \}
..

        下面的 getmac 版本允许用户将宏包的名字指定为第二个命令行参数。

#! /bin/bash
# getmac - read macro definition for $1 from package $2
file=/usr/lib/macros/mmt
mac="$1"
case $2 in
-ms) file="/work/macros/current/tmac.s";;
-mm) file="/usr/lib/macros/mmt";;
-man) file="/usr/lib/macros/an";;
esac
sed -n "/^\.de *$mac/,/^\.\.$/p" $file

(2)生成提纲的脚本 do.outline

        脚本内容:

sed -n '
s/"//g
s/^\.Se /CHAPTER /p
s/^\.Ah / A. /p
s/^\.Bh / B. /p' $*

        执行方式:

$ do.outline ch13/sect1
CHAPTER 13 Let the Computer Do the Dirty WorkA. Shell ProgrammingB. Stored CommandsB. Passing Arguments to Shell ScriptsB. Conditional ExecutionB. Discarding Used ArgumentsB. Repetitive ExecutionB. Setting Default ValuesB. What We've Accomplished

        do.outline 对在命令行上指定的所有文件($*)起作用。可以修改这个脚本以搜索任意种类的编码格式。例如:

sed -n '
s/[{}]//g
s/\\section/ A. /p
s/\\subsection/ B. /p' $*

4. 编辑工作转移

        在管道中进行编辑操作是 sed 作为真正的流编辑器的一个应用,这些编辑操作不会被写回到文件中。下面的例子是用脚本 format 将输入转换为 troff 能够处理的文本,具体是用 sed 处理输入,将一对连字符(--)替换为 troff 的 “\(em”。

        脚本 format 内容如下:

#! /bin/bash
eqn= pic= col=
files= options= roff="ditroff -Tps"
sed="| sed '/---/!s/--/\\(em/g'"
while [ $# -gt 0 ]
docase $1 in-E) eqn="| eqn";;-P) pic="| pic";;-N) roff="nroff" col="| col" sed= ;;-*) options="$options $1";;*) if [ -f $1 ]then files="$files $1"else echo "format: $1: file not found"; exit 1fi;;esacshift
done
eval "cat $files $sed | tbl $eqn $pic | $roff $options $col | lp"

        当对一个文档排版时,将连字符换成长破折号不是唯一要做的“美化”工作,因为有许多涉及到标点符号、空格和制表符等各种情况。脚本可能看上去如下所示:

s/^"/``/
s/"$/''/
s/"? /''? /g
s/"?$/''?/g
s/ "/ ``/g
s/" /'' /g
s/ "/ ``/g
s/" /'' /g
s/")/'')/g
s/"]/'']/g
s/("/(``/g
s/\["/\[``/g
s/";/'';/g
s/":/'':/g
s/,"/,''/g
s/",/'',/g
s/\."/.\\\&''/g
s/"\./''.\\\&/g
s/\\(em\\^"/\\(em``/g
s/"\\(em/''\\(em/g
s/\\(em"/\\(em``/g
s/@DQ@/"/g

五、几个使用 sed 脚本的提示

  1. 设计脚本前使用 grep 仔细检查输入文件,充分了解输入。
  2. 从测试文件中的小示例开始。在示例上运行脚本并且确信脚本能正常工作。记住,确保脚本在不想让它工作的地方不能工作同样重要。然后增加示例的规模,试着增加输入的复杂性。
  3. 仔细测试脚本中的每个命令,比较输入和输出文件看看发生了什么变化,亲自证明脚本是完整的。确认在输入文件正确的前提下,脚本可以正确地工作,而不仅仅是认为可以。
  4. 尝试用 sed 脚本完成工作,但不必100%。遇到困难时检查它们发生的频繁程度,有时手动来完成剩下的几个编辑工作比较好。

这篇关于sed awk 第二版学习(三)—— 编写 sed 脚本的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1138583

相关文章

Linux下MySQL数据库定时备份脚本与Crontab配置教学

《Linux下MySQL数据库定时备份脚本与Crontab配置教学》在生产环境中,数据库是核心资产之一,定期备份数据库可以有效防止意外数据丢失,本文将分享一份MySQL定时备份脚本,并讲解如何通过cr... 目录备份脚本详解脚本功能说明授权与可执行权限使用 Crontab 定时执行编辑 Crontab添加定

Unity新手入门学习殿堂级知识详细讲解(图文)

《Unity新手入门学习殿堂级知识详细讲解(图文)》Unity是一款跨平台游戏引擎,支持2D/3D及VR/AR开发,核心功能模块包括图形、音频、物理等,通过可视化编辑器与脚本扩展实现开发,项目结构含A... 目录入门概述什么是 UnityUnity引擎基础认知编辑器核心操作Unity 编辑器项目模式分类工程

Java调用Python脚本实现HelloWorld的示例详解

《Java调用Python脚本实现HelloWorld的示例详解》作为程序员,我们经常会遇到需要在Java项目中调用Python脚本的场景,下面我们来看看如何从基础到进阶,一步步实现Java与Pyth... 目录一、环境准备二、基础调用:使用 Runtime.exec()2.1 实现步骤2.2 代码解析三、

Python脚本轻松实现检测麦克风功能

《Python脚本轻松实现检测麦克风功能》在进行音频处理或开发需要使用麦克风的应用程序时,确保麦克风功能正常是非常重要的,本文将介绍一个简单的Python脚本,能够帮助我们检测本地麦克风的功能,需要的... 目录轻松检测麦克风功能脚本介绍一、python环境准备二、代码解析三、使用方法四、知识扩展轻松检测麦

基于Python编写自动化邮件发送程序(进阶版)

《基于Python编写自动化邮件发送程序(进阶版)》在数字化时代,自动化邮件发送功能已成为企业和个人提升工作效率的重要工具,本文将使用Python编写一个简单的自动化邮件发送程序,希望对大家有所帮助... 目录理解SMTP协议基础配置开发环境构建邮件发送函数核心逻辑实现完整发送流程添加附件支持功能实现htm

Python学习笔记之getattr和hasattr用法示例详解

《Python学习笔记之getattr和hasattr用法示例详解》在Python中,hasattr()、getattr()和setattr()是一组内置函数,用于对对象的属性进行操作和查询,这篇文章... 目录1.getattr用法详解1.1 基本作用1.2 示例1.3 原理2.hasattr用法详解2.

基于Python Playwright进行前端性能测试的脚本实现

《基于PythonPlaywright进行前端性能测试的脚本实现》在当今Web应用开发中,性能优化是提升用户体验的关键因素之一,本文将介绍如何使用Playwright构建一个自动化性能测试工具,希望... 目录引言工具概述整体架构核心实现解析1. 浏览器初始化2. 性能数据收集3. 资源分析4. 关键性能指

shell脚本批量导出redis key-value方式

《shell脚本批量导出rediskey-value方式》为避免keys全量扫描导致Redis卡顿,可先通过dump.rdb备份文件在本地恢复,再使用scan命令渐进导出key-value,通过CN... 目录1 背景2 详细步骤2.1 本地docker启动Redis2.2 shell批量导出脚本3 附录总

Oracle数据库定时备份脚本方式(Linux)

《Oracle数据库定时备份脚本方式(Linux)》文章介绍Oracle数据库自动备份方案,包含主机备份传输与备机解压导入流程,强调需提前全量删除原库数据避免报错,并需配置无密传输、定时任务及验证脚本... 目录说明主机脚本备机上自动导库脚本整个自动备份oracle数据库的过程(建议全程用root用户)总结

linux下shell脚本启动jar包实现过程

《linux下shell脚本启动jar包实现过程》确保APP_NAME和LOG_FILE位于目录内,首次启动前需手动创建log文件夹,否则报错,此为个人经验,供参考,欢迎支持脚本之家... 目录linux下shell脚本启动jar包样例1样例2总结linux下shell脚本启动jar包样例1#!/bin