linuxsed命令使用教程,sed命令在linux中的作用

首页 > 实用技巧 > 作者:YD1662023-11-18 03:38:30

The Sed uppercase `Next` command

这些命令的使用场景主要用于实现队列( FIFO 列表 )。从一个输入文件中删除最后 5 行就是一个很权威的例子:

cat -n inputfile | sed -En -e '

1 { N;N;N;N } # 确保模式空间中包含 5 行

N # 追加第 6 行到队列中

P # 输出队列的第 1 行

D # 删除队列的第 1 行

'

作为第二个示例,我们可以在两个列上显示输入数据:

# 输出两列

sed < inputfile -En -e '

$!N # 追加一个新行到模式空间

# 除了输入文件的最后一行

# 当在输入文件的最后一行使用 N 命令时

# GNU Sed 和 POSIX Sed 的行为是有差异的

# 需要使用一个技巧去处理这种情况

# https://www.gnu.org/software/sed/manual/sed.html#N_005fcommand_005flast_005fline

# 用空间填充第 1 行的第 1 个字段

# 并丢弃其余行

s/:.*\n/ \n/

s/:.*// # 除了第 2 行上的第 1 个字段外,丢弃其余的行

s/(.{20}).*\n/\1/ # 修剪并连接行

p # 输出结果

'

分支

我们刚才已经看到,Sed 因为有保持空间所以有了缓存的功能。其实它还有测试和分支的指令。因为有这些特性使得 Sed 是一个 图灵完备 的语言。虽然它可能看起来很傻,但意味着你可以使用 Sed 写任何程序。你可以实现任何你的目的,但并不意味着实现起来会很容易,而且结果也不一定会很高效。

不过不用担心。在本文中,我们将使用能够展示测试和分支功能的最简单的例子。虽然这些功能乍一看似乎很有限,但请记住,有些人用 Sed 写了 http://www.catonmat.net/ftp/sed/dc.sed [计算器]、 http://www.catonmat.net/ftp/sed/sedtris.sed [俄罗斯方块] 或许多其它类型的应用程序!

标签和分支

从某些方面,你可以将 Sed 看到是一个功能有限的汇编语言。因此,你不会找到在高级语言中常见的 “for” 或 “while” 循环,或者 “if … else” 语句,但是你可以使用分支来实现同样的功能。

linuxsed命令使用教程,sed命令在linux中的作用(13)

The Sed branch command

如果你在本文开始部分看到了用流程图描述的 Sed 运行模型,那么你应该知道 Sed 会自动增加程序计数器(PC)的值,命令是按程序的指令顺序来运行的。但是,使用分支(b)指令,你可以通过选择执行程序中的任意命令来改变顺序运行的程序。跳转目的地是使用一个标签(:)来显式定义的。

linuxsed命令使用教程,sed命令在linux中的作用(14)

The Sed label command

这是一个这样的示例:

echo hello | sed -ne '

:start # 在程序的该行上放置一个 “start” 标签

p # 输出模式空间内容

b start # 继续在 :start 标签上运行

' | less

那个 Sed 程序的行为非常类似于 yes 命令:它获取一个字符串并产生一个包含那个字符串的无限流。

切换到一个标签就像我们绕开了 Sed 的自动化特性一样:它既不读取任何输入,也不输出任何内容,更不更新任何缓冲区。它只是跳转到源程序指令顺序中下一条的另外一个指令。

值得一提的是,如果在分支命令(b)上没有指定一个标签作为它的参数,那么分支将直接切换到程序结束的地方。因此,Sed 将启动一个新的循环。这个特性可以用于去跳过一些指令并且因此可以用于作为“块”的替代者:

cat -n inputfile | sed -ne '

/usb/!b

/daemon/!b

p

'

条件分支

到目前为止,我们已经看到了无条件分支,这个术语可能有点误导嫌疑,因为 Sed 命令总是基于它们的可选地址来作为条件的。

但是,在传统意义上,一个无条件分支也是一个分支,当它运行时,将跳转到特定的目的地,而条件分支既有可能也或许不可能跳转到特定的指令,这取决于系统的当前状态。

Sed 只有一个条件指令,就是测试(t)命令。只有在当前循环的开始或因为前一个条件分支运行了替换,它才跳转到不同的指令。更多的情况是,只有替换标志被设置时,测试命令才会切换分支。

linuxsed命令使用教程,sed命令在linux中的作用(15)

The Sed `test` command

使用测试指令,你可以在一个 Sed 程序中很轻松地执行一个循环。作为一个特定的示例,你可以用它将一个行填充到某个长度(这是使用正则表达式无法实现的):

# 居中文本

cut -d: -f1 inputfile | sed -Ee '

:start

s/^(.{,19})$/ \1 / # 用一个空格填充少于 20 个字符的行的开始处

# 并在结束处添加另一个空格

t start # 如果我们已经添加了一个空格,则返回到 :start 标签

s/(.{20}).*/| \1 |/ # 只保留一个行的前 20 个字符

# 以修复由于奇数行引起的差一错误

'

如果你仔细读前面的示例,你可能注意到,在将要把数据“喂”给 Sed 之前,我通过 cut 命令做了一点小修正去预处理数据。

不过,我们也可以只使用 Sed 对程序做一些小修改来执行相同的任务:

cat inputfile | sed -Ee '

s/:.*// # 除第 1 个字段外删除剩余字段

t start

:start

s/^(.{,19})$/ \1 / # 用一个空格填充少于 20 个字符的行的开始处

# 并在结束处添加另一个空格

t start # 如果我们已经添加了一个空格,则返回到 :start 标签

s/(.{20}).*/| \1 |/ # 仅保留一个行的前 20 个字符

# 以修复由于奇数行引起的差一错误

'

在上面的示例中,你或许对下列的结构感到惊奇:

t start

:start

乍一看,在这里的分支并没有用,因为它只是跳转到将要运行的指令处。但是,如果你仔细阅读了测试命令的定义,你将会看到,如果在当前循环的开始或者前一个测试命令运行后发生了一个替换,分支才会起作用。换句话说就是,测试指令有清除替换标志的副作用。这也正是上面的代码片段的真实目的。这是一个在包含条件分支的 Sed 程序中经常看到的技巧,用于在使用多个替换命令时避免出现 误报(false positive)的情况。

通过它并不能绝对强制地清除替换标志,我同意这一说法。因为在将字符串填充到正确的长度时我使用的特定的替换命令是 幂等(idempotent)的。因此,一个多余的迭代并不会改变结果。不过,我们可以现在再次看一下第二个示例:

# 基于它们的登录程序来分类用户帐户

cat inputfile | sed -Ene '

s/^/login=/

/nologin/s/^/type=SERV /

/false/s/^/type=SERV /

t print

s/^/type=USER /

:print

s/:.*//p

'

我希望在这里根据用户默认配置的登录程序,为用户帐户打上 “SERV” 或 “USER” 的标签。如果你运行它,预计你将看到 “SERV” 标签。然而,并没有在输出中跟踪到 “USER” 标签。为什么呢?因为 t print 指令不论行的内容是什么,它总是切换,替换标志总是由程序的第一个替换命令来设置。一旦替换标志设置完成后,在下一个行被读取或直到下一个测试命令之前,这个标志将保持不变。下面我们给出修复这个程序的解决方案:

# 基于用户登录程序来分类用户帐户

cat inputfile | sed -Ene '

s/^/login=/

t classify # clear the "substitution flag"

:classify

/nologin/s/^/type=SERV /

/false/s/^/type=SERV /

t print

s/^/type=USER /

:print

s/:.*//p

'

精确地处理文本

Sed 是一个非交互式文本编辑器。虽然是非交互式的,但仍然是文本编辑器。而如果没有在输出中插入一些东西的功能,那它就不算一个完整的文本编辑器。我不是很喜欢它的文本编辑的特性,因为我发现它的语法太难用了(即便是以 Sed 的标准而言),但有时你难免会用到它。

采用严格的 POSIX 语法的只有三个命令:改变(c)、插入(i)或追加(a)一些文字文本到输出,都遵循相同的特定语法:命令字母后面跟着一个反斜杠,并且文本从脚本的下一行上开始插入:

head -5 inputfile | sed '

1i\

# List of user accounts

$a\

# end

'

插入多行文本,你必须每一行结束的位置使用一个反斜杠:

head -5 inputfile | sed '

1i\

# List of user accounts\

# (users 1 through 5)

$a\

# end

'

一些 Sed 实现,比如 GNU Sed,在初始的反斜杠后面的换行符是可选的,即便是在 --posix 模式下仍然如此。我在标准中并没有找到任何关于该替代语法的说明(如果是因为我没有在标准中找到那个特性,请在评论区留言告诉我!)。因此,如果对可移植性要求很高,请注意使用它的风险:

# 非 POSIX 语法:

head -5 inputfile | sed -e '

1i\# List of user accounts

$a\# end

'

也有一些 Sed 的实现,让初始的反斜杠完全是可选的。因此毫无疑问,它是一个厂商对 POSIX 标准进行扩展的特定版本,它是否支持那个语法,你需要去查看那个 Sed 版本的手册。

在简单概述之后,我们现在来回顾一下这些命令的更多细节,从我还没有介绍的改变命令开始。

改变命令

改变命令(c\)就像 d 命令一样删除模式空间的内容并开始一个新的循环。唯一的不同在于,当命令运行之后,用户提供的文本是写往输出的。

linuxsed命令使用教程,sed命令在linux中的作用(16)

上一页12345下一页

栏目热文

文档排行

本站推荐

Copyright © 2018 - 2021 www.yd166.com., All Rights Reserved.