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

相关文章

利用Python编写一个简单的聊天机器人

《利用Python编写一个简单的聊天机器人》这篇文章主要为大家详细介绍了如何利用Python编写一个简单的聊天机器人,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 使用 python 编写一个简单的聊天机器人可以从最基础的逻辑开始,然后逐步加入更复杂的功能。这里我们将先实现一个简单的

java脚本使用不同版本jdk的说明介绍

《java脚本使用不同版本jdk的说明介绍》本文介绍了在Java中执行JavaScript脚本的几种方式,包括使用ScriptEngine、Nashorn和GraalVM,ScriptEngine适用... 目录Java脚本使用不同版本jdk的说明1.使用ScriptEngine执行javascript2.

解决Cron定时任务中Pytest脚本无法发送邮件的问题

《解决Cron定时任务中Pytest脚本无法发送邮件的问题》文章探讨解决在Cron定时任务中运行Pytest脚本时邮件发送失败的问题,先优化环境变量,再检查Pytest邮件配置,接着配置文件确保SMT... 目录引言1. 环境变量优化:确保Cron任务可以正确执行解决方案:1.1. 创建一个脚本1.2. 修

使用PyQt5编写一个简单的取色器

《使用PyQt5编写一个简单的取色器》:本文主要介绍PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16进制颜色编码,一款跟随鼠标刷新图像的RGB和16... 目录取色器1取色器2PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16

python写个唤醒睡眠电脑的脚本

《python写个唤醒睡眠电脑的脚本》这篇文章主要为大家详细介绍了如何使用python写个唤醒睡眠电脑的脚本,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 环境:win10python3.12问题描述:怎么用python写个唤醒睡眠电脑的脚本?解决方案:1.唤醒处于睡眠状

多模块的springboot项目发布指定模块的脚本方式

《多模块的springboot项目发布指定模块的脚本方式》该文章主要介绍了如何在多模块的SpringBoot项目中发布指定模块的脚本,作者原先的脚本会清理并编译所有模块,导致发布时间过长,通过简化脚本... 目录多模块的springboot项目发布指定模块的脚本1、不计成本地全部发布2、指定模块发布总结多模

shell脚本快速检查192.168.1网段ip是否在用的方法

《shell脚本快速检查192.168.1网段ip是否在用的方法》该Shell脚本通过并发ping命令检查192.168.1网段中哪些IP地址正在使用,脚本定义了网络段、超时时间和并行扫描数量,并使用... 目录脚本:检查 192.168.1 网段 IP 是否在用脚本说明使用方法示例输出优化建议总结检查 1

Linux使用nohup命令在后台运行脚本

《Linux使用nohup命令在后台运行脚本》在Linux或类Unix系统中,后台运行脚本是一项非常实用的技能,尤其适用于需要长时间运行的任务或服务,本文我们来看看如何使用nohup命令在后台... 目录nohup 命令简介基本用法输出重定向& 符号的作用后台进程的特点注意事项实际应用场景长时间运行的任务服

如何使用 Bash 脚本中的time命令来统计命令执行时间(中英双语)

《如何使用Bash脚本中的time命令来统计命令执行时间(中英双语)》本文介绍了如何在Bash脚本中使用`time`命令来测量命令执行时间,包括`real`、`user`和`sys`三个时间指标,... 使用 Bash 脚本中的 time 命令来统计命令执行时间在日常的开发和运维过程中,性能监控和优化是不

bat脚本启动git bash窗口,并执行命令方式

《bat脚本启动gitbash窗口,并执行命令方式》本文介绍了如何在Windows服务器上使用cmd启动jar包时出现乱码的问题,并提供了解决方法——使用GitBash窗口启动并设置编码,通过编写s... 目录一、简介二、使用说明2.1 start.BAT脚本2.2 参数说明2.3 效果总结一、简介某些情