⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 471.html

📁 著名的linux英雄站点的文档打包
💻 HTML
📖 第 1 页 / 共 5 页
字号:
随着对 sed 越来越熟悉,您可以花最小力气来进行相当强大的文本处理。您可能想如何使用熟悉的脚本语言来处理这种问题 -- 能用一行代码轻易实现这样的解决方案吗?<br>
<br>
组合使用<br>
在开始创建更复杂的 sed 脚本时,需要有输入多个命令的能力。有几种方法这样做。首先,可以在命令之间使用分号。例如,以下命令系列使用 '=' 命令和 'p' 命令,'=' 命令告诉 sed 打印行号,'p' 命令明确告诉 sed 打印该行(因为处于 '-n' 模式)。<br>
<br>
 $ sed -n -e '=;p' myfile.txt <br>
无论什么时候指定了两个或更多命令,都按顺序将每个命令应用到文件的每一行。在上例中,首先将 '=' 命令应用到第 1 行,然后应用 'p' 命令。接着,sed 继续处理第 2 行,并重复该过程。虽然分号很方便,但是在某些场合下,它不能正常工作。另一种替换方法是使用两个 -e 选项来指定两个不同的命令:<br>
<br>
 $ sed -n -e '=' -e 'p' myfile.txt <br>
然而,在使用更为复杂的附加和插入命令时,甚至多个 '-e' 选项也不能帮我们的忙。对于复杂的多行脚本,最好的方法是将命令放入一个单独的文件中。然后,用 -f 选项引用该脚本文件:<br>
<br>
 $ sed -n -f mycommands.sed myfile.txt <br>
这种方法虽然可能不太方便,但总是管用。<br>
<br>
一个地址的多个命令<br>
有时,可能要指定应用到一个地址的多个命令。这在执行许多 's///' 以变换源文件中的字和语法时特别方便。要对一个地址执行多个命令,可在文件中输入 sed 命令,然后使用 '{ }' 字符将这些命令分组,如下所示:<br>
<br>
 1,20{ 	s/[Ll]inux/GNU/Linux/g 	s/samba/Samba/g 	s/posix/POSIX/g } <br>
上例将把三个替换命令应用到第 1 行到第 20 行(包括这两行)。还可以使用规则表达式地址或者二者的组合:<br>
<br>
 1,/^END/{         s/[Ll]inux/GNU/Linux/g         s/samba/Samba/g         s/posix/POSIX/g 	p } <br>
该例将把 '{ }' 之间的所有命令应用到从第 1 行开始,到以字母 "END" 开始的行结束(如果在源文件中没发现 "END",则到文件结束)的所有行。<br>
<br>
附加、插入和更改行<br>
既然在单独的文件中编写 sed 脚本,我们可以利用附加、插入和更改行命令。这些命令将在当前行之后插入一行,在当前行之前插入一行,或者替换模式空间中的当前行。它们也可以用来将多行插入到输出。插入行命令用法如下:<br>
<br>
 i This line will be inserted before each line <br>
如果不为该命令指定地址,那么它将应用到每一行,并产生如下的输出:<br>
<br>
 This line will be inserted before each line line 1 here <br>
 This line will be inserted before each line line 2 here <br>
 This line will be inserted before each line line 3 here <br>
 This line will be inserted before each line line 4 here <br>
如果要在当前行之前插入多行,可以通过在前一行之后附加一个反斜杠来添加附加行,如下所示:<br>
<br>
 i insert this line and this one and this one and, uh, this one too. <br>
附加命令的用法与之类似,但是它将把一行或多行插入到模式空间中的当前行之后。其用法如下:<br>
<br>
 a insert this line after each line.  Thanks! :) <br>
另一方面,“更改行”命令将实际替换模式空间中的当前行,其用法如下:<br>
<br>
 c You're history, original line! Muhahaha! <br>
因为附加、插入和更改行命令需要在多行输入,所以将把它们输入到一个文本 sed 脚本中,然后通过使用 '-f' 选项告诉 sed 执行它们。使用其它方法将命令传递给 sed 会出现问题。<br>
<br>
下一篇<br>
在下一篇、也是本 sed 系列的最后一篇文章中,我将为您演示许多使用 sed 来完成不同类型任务的极佳实例。我将不仅为您显示脚本做些什么,还显示为什么那样做。完成之后,您将掌握更多有关如何在不同项目中使用 sed 的极佳知识。到时候见!<br>
<br>
<br>
在这篇 sed 系列的总结性文章中,Daniel Robbins 带您体验 sed 的真正力量。在介绍完几个重要的 sed 脚本之后,他将通过将一个 Quicken .QIF 文件转换成可读文本格式来演示一些基本 sed 脚本的编写。该转换脚本不仅实用,而且还是展现 sed 脚本编写能力的极佳示例。<br>
<br>
强健的 sed<br>
在第二篇 sed 文章中,我提供了一些示例来演示 sed 的工作原理,但是它们当中很少有示例能实际做特别有用的事。在这篇 sed 系列的最后文章中,我要改变那种方式,并使用 sed 来做实际的事。我将为您显示几个示例,它们不仅演示 sed 的能力,而且还做一些真正巧妙(和方便)的事。例如,在本文的后半部,将为您演示如何设计一个 sed 脚本来将 .QIF 文件从 Intuit 的 Quicken 金融程序转换成具有良好格式的文本文件。在那样做之前,我们将看一下不怎么复杂但却很有用的 sed 脚本。<br>
<br>
文本转换<br>
第一个实际脚本将 UNIX 风格的文本转换成 DOS/Windows 格式。您可能知道,基于 DOS/Windows 的文本文件在每一行末尾有一个 CR(回车)和 LF(换行),而 UNIX 文本只有一个换行。有时可能需要将某些 UNIX 文本移至 Windows 系统,该脚本将为您执行必需的格式转换。<br>
<br>
 $ sed -e 's/$/
/' myunix.txt &gt; mydos.txt <br>
 <br>
<br>
<br>
在该脚本中,'$' 规则表达式将与行的末尾匹配,而 '
' 告诉 sed 在其之前插入一个回车。在换行之前插入回车,立即,每一行就以 CR/LF 结束。请注意,仅当使用 GNU sed 3.02.80 或以后的版本时,才会用 CR 替换 '
'。如果还没有安装 GNU sed 3.02.80,请在我的第一篇 sed 文章中查看如何这样做的说明。<br>
<br>
我已记不清有多少次在下载一些示例脚本或 C 代码之后,却发现它是 DOS/Windows 格式。虽然很多程序不在乎 DOS/Windows 格式的 CR/LF 文本文件,但是有几个程序却在乎 -- 最著名的是 bash,只要一遇到回车,它就会出问题。以下 sed 调用将把 DOS/Windows 格式的文本转换成可信赖的 UNIX 格式:<br>
<br>
 $ sed -e 's/.$//' mydos.txt &gt; myunix.txt <br>
 <br>
<br>
<br>
该脚本的工作原理很简单:替代规则表达式与一行的最末字符匹配,而该字符恰好就是回车。我们用空字符替换它,从而将其从输出中彻底删除。如果使用该脚本并注意到已经删除了输出中每行的最末字符,那么,您就指定了已经是 UNIX 格式的文本文件。也就没必要那样做了!<br>
<br>
反转行<br>
下面是另一个方便的小脚本。与大多数 Linux 发行版中包括的 "tac" 命令一样,该脚本将反转文件中行的次序。"tac" 这个名称可能会给人以误导,因为 "tac" 不反转行中字符的位置(左和右),而是反转文件中行的位置(上和下)。用 "tac" 处理以下文件:<br>
<br>
 foo bar oni <br>
 <br>
<br>
<br>
....将产生以下输出:<br>
<br>
 oni bar foo <br>
 <br>
<br>
<br>
可以用以下 sed 脚本达到相同目的: <br>
<br>
 $ sed -e '1!G;h;$!d' forward.txt &gt; backward.txt <br>
 <br>
<br>
<br>
如果登录到恰巧没有 "tac" 命令的 FreeBSD 系统,将发现该 sed 脚本很有用。虽然方便,但最好还是知道该脚本为什么那样做。让我们对它进行讨论。<br>
<br>
反转解释<br>
首先,该脚本包含三个由分号隔开的单独 sed 命令:'1!G'、'h' 和 '$!d'。现在,需要好好理解用于第一个和第三个命令的地址。如果第一个命令是 '1G',则 'G' 命令将只应用第一行。然而,还有一个 '!' 字符 -- 该 '!' 字符忽略该地址,即,'G' 命令将应用到除第一行之外的所有行。'$!d' 命令与之类似。如果命令是 '$d',则将只把 'd' 命令应用到文件中的最后一行('$' 地址是指定最后一行的简单方式)。然而,有了 '!' 之后,'$!d' 将把 'd' 命令应用到除最后一行之外的所有行。现在,我们所要理解的是这些命令本身做什么。<br>
<br>
当对上面的文本文件执行反转脚本时,首先执行的命令是 'h'。该命令告诉 sed 将模式空间(保存正在处理的当前行的缓冲区)的内容复制到保留空间(临时缓冲区)。然后,执行 'd' 命令,该命令从模式空间中删除 "foo",以便在对这一行执行完所有命令之后不打印它。<br>
<br>
现在,第二行。在将 "bar" 读入模式空间之后,执行 'G' 命令,该命令将保留空间的内容 ("foo") 附加到模式空间 ("bar"),使模式空间的内容为 "barfoo"。'h' 命令将该内容放回保留空间保护起来,然后,'d' 从模式空间删除该行,以便不打印它。<br>
<br>
对于最后的 "oni" 行,除了不删除模式空间的内容(由于 'd' 之前的 '$!')以及将模式空间的内容(三行)打印到标准输出之外,重复同样的步骤。<br>
<br>
现在,要用 sed 执行一些强大的数据转换。<br>
<br>
sed QIF 魔法<br>
过去几个星期,我一直想买一份 Quicken 来结算我的银行帐户。Quicken 是一个非常好的金融程序,当然会成功地完成这项工作。但是,经过考虑之后,我觉得自己可以轻易编写某个软件来结算我的支票簿。我想,毕竟,我是个软件开发人员!<br>
<br>
我开发了一个很好的小型支票簿结算程序(使用 awk),它通过分析包含我的所有交易的文本文件的语法来计算余额。略微调整之后,我将其改进,以便可以象 Quicken 那样跟踪不同的贷款和借款类别。但是,我还要添加一个特性。最近,我将帐户转移到一家有联机 Web 帐户界面的银行。有一天,我注意到,这家银行的 Web 站点允许以 Quicken 的 .QIF 格式下载我的帐户信息。我马上觉得,如果可以将该信息转换成文本格式,那就太棒了。<br>
<br>
两种格式的故事<br>
在查看 QIF 格式之前,先看一下我的 checkbook.txt 格式:<br>
<br>
 28 Aug 2000     food    -       -       Y     Supermarket             30.94 25 Aug 2000     watr    -       103     Y     Check 103               52.86 <br>
 <br>
<br>
<br>
在我的文件中,所有字段都由一个或多个制表符分开,每个交易占据一行。日期之后的下一个字段列出支出类型(如果是收入项,则为 "-")。第三个字段列出收入类型(如果是支出项,则为 "-")。然后,是一个支票号字段(如果为空,则还是 "-"),一个交易完成字段("Y" 或 "N"),一个注释和一个美元金额字段。现在,让我们看一下 QIF 格式。当用文本查看器查看下载的 QIF 文件时,它看起来如下:<br>
<br>
 !Type:Bank D08/28/2000 T-8.15 N PCHECKCARD SUPERMARKET ^ D08/28/2000 T-8.25 N PCHECKCARD PUNJAB RESTAURANT ^ D08/28/2000 T-17.17 N PCHECKCARD SUPERMARKET <br>
 <br>
<br>
<br>
浏览过文件之后,不难猜出其格式 -- 忽略第一行,其余的格式如下: <br>
<br>
 D&lt;数据&gt;  <br>
T&lt;交易量&gt;  <br>
N&lt;支票号&gt;  <br>
P&lt;描述&gt;  <br>
^   (这是字段分隔符) <br>
 <br>
<br>
<br>
开始处理<br>
在处理象这样重要的 sed 项目时,不要气馁 -- sed 允许您将数据逐渐修改成最终形式。在进行当中,可以继续细化 sed 脚本,直到输出与预期的完全一样为止。无需在试第一次时就保证其完全正确。<br>
<br>
要开始,首先创建一个名为 "qiftrans.sed" 的文件,然后开始修改数据: <br>
<br>
 1d /^^/d s/[[:cntrl:]]//g <br>
 <br>
<br>
<br>
第一个 '1d' 命令删除第一行,第二个命令从输出除去那些讨厌的 '^' 字符。最后一行除去文件中可能存在的任何控制字符。既然在处理外来文件格式,我想消除在中途遇到任何控制字符的风险。到目前为止,一切顺利。现在,要向该基本脚本中添加一些处理功能:<br>
<br>
 1d /^^/d s/[[:cntrl:]]//g /^D/ { <br>
 s/^D(.*)/1	OUTY	INNY	/ <br>
 s/^01/Jan/         s/^02/Feb/<br>
 s/^03/Mar/         s/^04/Apr/<br>
 s/^05/May/         s/^06/Jun/ <br>
 s/^07/Jul/         s/^08/Aug/ <br>
 s/^09/Sep/         s/^10/Oct/<br>
 s/^11/Nov/         s/^12/Dec/ <br>
 s:^(.*)/(.*)/(.*):2 1 3:  } <br>
 <br>
<br>
<br>
首先,添加一个 '/^D/' 地址,以便 sed 只在遇到 QIF 数据字段的第一个字符 'D' 时才开始处理。当 sed 将这样一行读入其模式空间时,将按顺序执行花括号中的所有命令。<br>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -