跳至主要内容

sed单行脚本学习笔记

Sed单行脚本学习笔记

redraiment, 2009-12-31

回家真好





  前段时间忙着找工作、项目结题、写报告……反正是总有做不完的事情,哈哈。好在暂时告一段落了,应老妈强烈要求回家休息几天。这次回家除了这身衣服,只带了一本《sed与awk》,我觉得这种小册子最适合茶余饭后休闲之用。如果你也有兴趣学 sed ,推荐你一起看《sed与awk》(可以在谷歌图书在线阅读英文版:D)。
  花了两天时间,看完了前面 sed 的部分。要掌握一个工具就要熟悉它的规则,man 等参考手册向我们介绍这些规则,教程则演示如何使用这些规则,但要将这些规则运用自如,还需要去理解别人的代码并尝试自己解决问题。在 SourceForge 上有份经典的文档:《SED单行脚本快速参考》(单行脚本要求命令行长度小于65个字符),由 Eric Pement 整理,Joe Hong 翻译,通篇阅读后获益良多,故撰此文和大家分享。

精彩脚本摘录

# 在每一行后面增加一空行
sed G
  在参考手册中,命令G的作用是“将换行符后的保持空间内容追加到模式空间”。就像前文提到的,看过教程后只是熟悉了规则,还不能将规则运用自如,我自己写的代码是:sed 's/$/\n/',就是因为我还不熟悉每个命令会对模式空间产生什么影响。所以看到这段参考代码时感觉眼前一亮:“原来还可以这样写!”
# 显示文件中的最后10行 (模拟“tail”)
sed -e :a -e '$q;N;11,$D;ba'
  假设文件有 N 行(N 大于10),显示最后10行也就意味着删除前的 N-10 行。在多行模式中,命令“D”可以删除模式空间中第一行;命令“N”可以将下一行追加到模式空间中,建立多行模式。因此问题转化为:“1)将整个文件的内容放入一个模式空间中;2)删除前 N-10 行。”其中问题1)通过控制语句“b”来解决:sed ':a; N; ba';至于问题二,模式“1,$”代表文本中的所有行,因此紧跟着的命令被执行N次,同理,模式“11,$”匹配后面的 N-10 行,因此“11,$D”一个执行了 N-10 次。
  其实,在 GNU sed 中,命令“$q”是可以删掉的,因为在最后一行执行命令“N”就会因出错而自动退出。
  另外,在 info 手册中也有一个解决方法:sed '1h;2,10{H;g};$q;1,9d;N;D'。他的思路差不多,只不过是将中间文本保持在“保持空间”而不是“模式空间”,因此无需通过控制语句来制造循环。但它繁琐一些,需要在两处指定地址范围。
# 显示文件中的最后2行(模拟“tail -2”命令)
sed '$!N;$!D'
  这条脚本也很精彩,命令“$!N”只能执行到倒数第二行,除了倒数第二行,命令“$!D”都能被执行,因此仅剩下最后两行未被删除。我的解决方法要麻烦一些:sed -n 'N;$p;D',命令“$p”只能执行在倒数第二行执行,并且输出最后两行。当文件只有一行时,两段脚本都没有输出。

我的解决方法

# 显示文件中的倒数第二行
sed -e '$!{h;d;}' -e x              # 当文件中只有一行时,输入空行
sed -e '1{$q;}' -e '$!{h;d;}' -e x  # 当文件中只有一行时,显示该行
sed -e '1{$d;}' -e '$!{h;d;}' -e x  # 当文件中只有一行时,不输出

# 我的解决方法
sed 'x;$!d'                         # 当文件中只有一行时,输入空行
sed '1h;1!x;$!d'                    # 当文件中只有一行时,显示该行
sed -n 'N;$P;D'                     # 当文件中只有一行时,不输出
  在解决这个问题上,参看代码显得有些繁琐。只有将每行都交换“模式空间”和“保持空间”的内容(命令“x”),并将除最后一行外所有内容删除(命令“$!d”),就能获得倒数第二行,因为“保持空间”初始化时为空,因此当文件中只有一行时输入空行;为了在文件中只有一行时能显示该行,要对第一行特殊照顾:覆盖保持空间;第三条命令你很熟悉,咋一看以为是上面“tail -2”的解决方法,它们很像,差别仅仅是“tail -2”中“p”是小写,此处是大写。
# 删除文件中的重复行,不管有无相邻。注意hold space所能支持的缓存
# 大小,或者使用GNU sed。
sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P'

# 上面的代码在 GNU sed v4.1.5 中不能正常工作,但在 v4.0.7 中却可以执行。
# 修改后
sed -n 'G; s/\n/&&/; /^\([^\n]*\n\).*\n\1/d; s/\n//; h; P'
  参考代码中运用了一个小技巧:用模式“[ -~]”来匹配所有可打印字符。可打印字符的ASCII范围是0x20-0x7F,而0x20和0x7F分别是空格和波浪线。但这个技巧不能在 GNU sed v4.1.5 中使用(但在 v4.0.7 中却可以使用)。为了使代码通用,需要改为“[^\n]”。
# 只保留多个相邻空行的第一行。并且删除文件顶部和尾部的空行。
# (模拟“cat -s”)
sed '/./,/^$/!d'        #方法1,删除文件顶部的空行,允许尾部保留一空行
sed '/^$/N;/\n$/D'      #方法2,允许顶部保留一空行,尾部不留空行
  在我的环境里测试,方法2尾部同样保留一个空行。

我的单行脚本

  我看的兴起,也设计了一个单行脚本。问题来源于设计宏替换器,比如C语言中有如下定义:
#define PRINT printf
PRINT("printf with PRINT");
  此时使用 s/PRINT/printf/g 就会把字符串中的“PRINT”也替换掉。因此需要使用一下脚本:
# 只替换不在字符串内的模式
sed -r 's/^|"[^"]*"/&\n/g; :a; s/(\n[^"]*)foo/\1bar/; ta; s/\n//g'

# 只替换字符串中的模式
sed -r 's/^|"[^"]*"/\n&/g; :a; s/(\n"[^"]*)1/\1x/; ta; s/\n//g'
  替换不在字符串内模式的原理是:
  1. 先在起始位置和字符串的第二个引号后面添加换行符(s/^|"[^"]*"/&\n/g);
  2. 替换所有将换行符和第一个引号之间的模式,这些字符都不在字符串里面(s/(\n[^"]*)foo/\1bar/);
  3. 迭代执行第二部,直到替换所有模式(ta);
  4. 删除所有换行符(s/\n//g)。
  其中换行符是作分隔符用,你也可以使用其他的、不在该行中的字符。替换字符串内模式的原理基本相同,只是分隔符放到字符串第一个引号的前面,并替换以引号开头的模式。下面是测试结果(将字符‘1’替换为‘x’):
$ cat string
123"123"123
111"44a"jjl
dad"111"ddd
"111"44"5555"
"111""""333"
1122
"1111"2211"1111"
4455
"9988"
"1155"
$ sed -r 's/^|"[^"]*"/&\n/g;:a;s/(\n[^"]*)1/\1x/;ta;s/\n//g' string
x23"123"x23
xxx"44a"jjl
dad"111"ddd
"111"44"5555"
"111""""333"
xx22
"1111"22xx"1111"
4455
"9988"
"1155"
$ sed -r 's/^|"[^"]*"/\n&/g;:a;s/(\n"[^"]*)1/\1x/;ta;s/\n//g' string
123"x23"123
111"44a"jjl
dad"xxx"ddd
"xxx"44"5555"
"xxx""""333"
1122
"xxxx"2211"xxxx"
4455
"9988"
"xx55"

  我的环境是 Debian Lenny + GNU sed 4.1.5。Windows版本可以到 GNUWin32 下载最新的 Sed for Windows,也可以发邮件向我索取 GNU sed.exe v4.0.7。GNU sed 自 v3.02.80 起可以使用转义字符'\t'来代替制表符,其他大部分他版本还不能识别'\t'的简写方式。下面摘录《sed与awk》中很好玩的一段话:
  像许多程序一样,sed脚本通常以开始都很小,并且写和读都很简单。在测试脚本时,可能会发现不适用于一般规则的特殊情况。为了解决这些问题,可以给脚本增加行,生成更长、更复杂并且更完整的脚本。虽然花费在细化脚本上的时间抵消了不用手动编辑而节省下来的时间,但至少在这段时间内,你的头脑被自己的这个似乎熟悉的想法占据:“看!计算机完成的。”
——《sed与awk》 P119

评论

此博客中的热门博文

AutoHotKey 新手入门教程

AutoHotKey 真是一个好玩的工具!短短几行代码就是先了“窗口置顶”、“窗口透明”等功能,之前我还特意为此装了好几个小工具,现在都可以卸掉了。闲来无事,就把 Quick Start 翻译了一下,我没有逐字逐句地翻译,有时候我嫌原文罗嗦就用自己的话概括地描述了一下。 原文地址:http://www.autohotkey.com/docs/Tutorial.htm 教程目录 创建脚本 启动程序 模拟鼠标键盘 操纵窗口 输入 变量与剪切板 循环 操纵文件 其他特性 创建脚本 每个脚本都是一个纯文本文件,由一些能被 AutoHotKey.exe 执行的命令组成。一个脚本可能还包含 热键 和 热字符串 。如果没有热键和热字符串,脚本在启动的时候就会从头依次执行到尾。 创建一个新的脚本: 下载 并安装 AutoHotkey。 右击鼠标,选择 新建 -> 文本文档 。 输入文件名并确保以 .ahk 结尾。例如:Test.ahk。 右击文件,选择 编辑脚本 。 输入以下内容:#space::Run www.google.com 上一行的第一个字符 "#" 代表键盘上的 Windows 键;所以 #space 表示在按住 Windows 键后再按空格键。"::" 后面的命令会在热键激活后执行,在本例中则会打开谷歌主页。继续按下面步骤操作,来执行这个脚本: 保存并关闭该文件。 双击该文件来启动它。在系统托盘里会出现一个新图标。 按下 Windows 和空格键,网页会在默认的浏览器里打开。 右击系统托盘里的绿色图标可以退出或编辑当前脚本。 注意: 可以同时启动多个脚本,并且在系统托盘里都会有一个相应的图标。 每个脚本都能定义多个 热键 和 热字符串 。 想让某个脚本开机即启动,可以将它的 快捷方式放到开始菜单的启动目录里 。 启动程序 命令 Run 可以运行程序、打开文档、网页链接或快捷键。请参看以下示例: Run Notepad Run C:\My Documents\Address List.doc Run C:\My Documents\My Shortcut.lnk Run www.yahoo.com Run mailto:someone@somedoma

Linux下用nc实现DuplexPipe

Linux下用nc实现DuplexPipe redraiment, 2010-01-25   nc 是一把网络的瑞士军刀,我以前在介绍 DuplexPipe 时也提到过,如果你没接触过它,可以先参看一下《 DuplexPipe二三事(二) 》。再来简单地介绍一下 DuplexPipe,顾名思义,它是一个“双向管道”。在 shell 中,我们通过“|”使用匿名管道,让前一条命令的输出作为后一条命令的输入;双向管道即在此基础上在加上“后一条命令的输入作为前一条命令的输入”。这是最初开发它的原因,但后来发现它更像是一个网络接口转换器,“DuplexPipe”这个名字反而不能体现它的功能。更多内容请参看 DuplexPipe系列文章 。 留言   今天网友 黄海 给我留言,他通过用 nc 的 -e 选项来执行 nc 本身来实现 DuplexPipe。留言原文如下: 哥们,你写的那个DuplexPipe, 我很欣赏。不过近日于网上逛发现此工具的功能竟然完全可以用netcat做到,有两种方法,我的博客上载了一种。简单描述如下: 在windows下: echo nc [ip] [port] > relay.bat nc -l -p [port2] -e relay.bat 其余的类推 第二种方法是用命名管道:(linux下) mknod backpipe p nc -l -p [port] 0<backpipe | nc [ip] [port12] | tee backpipe   其中选项 -e 的作用是: for NT:    -e prog        inbound program to exec [dangerous!!] for Linux: -e filename    program to exec after connect [dangerous!!] Windows下不行   在我开发 DuplexPipe 时确实考虑过功能会不会和 nc 重叠,当时只想着通过 shell 管道来连接,忘了 nc 自带了一个双向管道!我首先在 Vista 下做了测试,nc(win32) 是从 http://www.securityfocus.com/tools/139 下载。开启三个命令提示符,分别执行: 1) nc -l -p 1234 2)