4

是否可以在 sed 地址中进行简单的算术运算?从“地址”手册部分来看,答案似乎不是。但也许有解决方法?

例如,如何打印文件的倒数第二行?这会很酷,例如:

sed -n '$-1 p' file

但它显然不起作用......所以我通常必须进行多次 sed 调用,首先用于识别行,然后使用 shell 进行算术$((expr)),最后再次调用 sed。像这样:

sed -n "$(($(sed -n '$ =' file)-1)) p" file

是否有一种“更好”、更紧凑、更易读的方法来使用 sed 地址进行算术运算?


在一个严重的拖延时刻,我决定编写一个小脚本来快速更改 xterm 颜色方案。这个想法是你有.Xresources一个带有开始标记和结束标记的文件:

...
START_MARKER
...
END_MARKER
...

并且您想删除标记之间的所有内容,而不是标记本身。同样,做类似的事情会很棒:

sed '/START_MARKER/+1,/END_MARKER/-1 d' file

......但你不能!

4

6 回答 6

3

你是对的,一个人不能直接在 sed 1中做数学,甚至是地址。但是你可以使用一些技巧来做你想做的事:

倒数第二行:

$ seq 5 | sed -n -e '${ # On the last line
> g # Replace the buffer with the hold space
> p # and print it
> }
> h' # All lines, store the current line in the hold space.
4

之间STARTEND

$ cat test.in
1
START
2
3
END
4
$ cat test.in | sed '/^START$/,/^END$/{
> /^START$/d
> /^END$/d
> p
> }
> d'
2
3
$ cat test.in | sed -n -e '/^START$/,/^END$/!d' -e '/^START/d' -e '/^END$/d' -e p
2
3

我正在使用 BSD (mac) sed;在 GNU 系统上,您可以在行;之间使用而不是换行符。或将其粘贴在脚本中。

1:Sed 是图灵完备的,所以你可以做数学,但它充其量是笨拙的:http ://rosettacode.org/wiki/A%2BB#sed

是的,我知道,UUOC;仅用于说明

于 2012-09-08T21:41:38.347 回答
1

删除倒数第二行:

sed ':r;$!{N;br};s/\n[^\n]*\(\n[^\n]*\)$/\1/' file

删除标记内的所有内容:

sed ':r;$!{N;br};s/START_MARKER.*END_MARKER/START_MARKER\nEND_MARKER/' file

远非优雅,但有点工作。

正如评论中提到的那样,sed在线运行。但是,您可以使用命令将另一行读入模式空间N。这两行现在都在模式空间中,并用 . 分隔\nsed也有执行流控制的手段,即标签和条件/无条件分支。一切都记录在 中man sed这里也是完整的示例参考。在上面的代码r中是一个标签;$!{..}意思是“除了最后一行之外的所有地方,做..N;br读取另一行并无条件地r再次分支。因此,:r;$!{N;br}您将所有输入读入模式空间,然后您将其作为单行操作,\n并将输入的行分开。

于 2012-09-08T21:13:51.380 回答
1

这可能对你有用(GNU sed);

sed '$!N;$s/.*\n//;P;D' file

这很有效,应该很容易理解:

sed '/start/,/end/!d;//d' file

这些是您问题的解决方案,但至于算术最好使用 awk 或 perl。

于 2012-09-08T22:22:19.077 回答
0

您有一些很好sed的建议,这里有一个基于 GNU 的建议awk

awk -v RS='START_MARKER|END_MARKER' 'RT == "END_MARKER"' infile
  • RS='START_MARKER|END_MARKER'使用标记作为分隔符分割输入。
  • RT设置为匹配的分隔符,当它匹配“END”时{print $0}执行默认块。

因此,例如,如果您想打印除最后三行之外的所有行,请将 FS 设置为\n并应用适当的循环:

awk -v RS='START_MARKER|END_MARKER' -v FS='\n' 'RT == "END" { for(i=1; i<NF-3; i++) print $i }' infile
于 2012-09-08T22:26:44.503 回答
-1

您可以使用简单的方法显示文件的倒数第二行。

TOTAL_LENGTH=$(cat file_name | wc -l)
SECOND_LAST_LINE=`expr $TOTAL_LENGTH - 1`
head -$SECOND_LAST_LINE | tail -1

如果要从文件中删除倒数第二行:

sed -i "$SECOND_LAST_LINE"d file_name
于 2012-09-08T21:18:55.027 回答
-1

sed解决方案#2中给出了更全面的算术处理方法。此处介绍了使用sed`sed' 自己的脚本。

由于过度“挥手”的不切实际的评论要求在解决方案#2中引起的大脑疼痛压力实际上是过多的代码“挥手”,并列,这是解决方案#3:

echo -e 'a\nb\nc\nd\ne' | sed -n '1!G;h;$p' | sed -n 3p

它仍然使用管道(“但也许有一种解决方法?”),其中数字 3 必须从文件 ala 的末尾“手动”替换为所需的行$-3

假设sed脚本是 '$-4 p; $-6p; $-8 p;'

echo -e 'a\nb\nc\nd\ne\nf\ng\nh\ni'  | 
sed  -n '1!G;h;$p'                   | 
sed  -n '4 p; 6p; 8 p;'              | 
sed  -n '1!G;h;$p'                   

通过做这项工作

echo  '$-4 p;  $-6p;  $-8 p;'  |   sed s/$-//

警告:
命令sed必须像print 一样简单。
“简单算术”只能是 '$-n' 的形式。
算术不是“正常”计算的。

一个“单个”“sed”命令字符串(如果之前的管道被视为这样,则为“行”)将嵌入并组合这两个命令,如下一个答案 #2 中所述。


致命一击。

鉴于这里第一个答案的敷衍解雇是#2:

由于这只是第二次或第三次编写大量sed脚本,严重的语法微妙(s)规避破坏解决方案似乎就足够了:ala

# file prep 
echo -e '  a\n  b\n  c\n  d\n  e\n  f' >test

下面的删除线不是不正确的,但是在播放和“搞砸”sed 这里的 SO 问题之后,如果从模式缓冲区运行以通过以下方式获取文件长度行数,则sed execute 可以更简单,无需 IO 重定向$

sed  -e  '1{h; s/.*/sed -n  "$="  test  /e' -e 'p;x}; ${p;x;}' test

枚举从一$=开始就保存在保持缓冲区中,并在最后再次打印。

# get "sed -n  $= test" command output into sed script
sed -n '1esed -n  "$=" test >sedr' test

# see where this is headed? so far "sed -n ... test" is irrelevant
# a pedantic "sed" only solution would keep it this way with
# all the required "sed"'ng as part of an 'e' command or '$e'
# where the 'sedr' file is itself "sed"'d ultimately to a final
# command 'sed -n /<the calculated line number>/p' 

# one could quibble whether '>sedr' io redirection is "pure sed" 

# modify 'sedr'with [the sed RPN][1] to get <the calculated line number>
# with judicious use of "sed"'s 'r' command and buffering will
# realize the effective script to compute the desired result

# this is left as an exercise needing perverse persistence with
# a certain amount of masochistic agony

作为如何进行的提示;使用解决方案#3 的技术,sed脚本$-地址现在被替换为$=值 and -。所以sed又被用来编辑自己的脚本。

解析sed脚本必须准确地修改$-in 地址。

此外,要使用 RPN 计算器,中缀算术必须具有后置固定运算符。在自动机和形式语言理论中,将波兰表示法或其反转转换为中缀是一种传统范式,反之亦然。

希望这可以确定可以做到的肯定答案(mais,pas par moi)和否定的答案,即它不是微不足道的练习(c'est par moi)。

任意解决方案的令人痛苦的理由在最后。

用于经验测试的环境:

linuxuser@ubuntu:~$ sed --version
sed (GNU sed) 4.4
Copyright (C) 2017 Free Software Foundation, Inc.

linuxuser@ubuntu:~$  uname -a
Linux ubuntu 4.15.0-74-generic #84-Ubuntu SMP Thu Dec 19 08:06:00 UTC 2019 i686 i686 i686 GNU/Linux

linuxuser@ubuntu:~$ lsbname -a
lsbname: command not found
linuxuser@ubuntu:~$ apropos lsb
lsb_release (1)      - print distribution-specific information
lsblk (8)            - list block devices
linuxuser@ubuntu:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.2 LTS
Release:    18.04
Codename:   bionic

解决方案#1

一种在盒子外面思考的技术:

seq 60  |  sed -n '$!p'  |  sed -n '$!p'  |  sed -n '$!p' |  sed -n '$p'

打印:

57

具体来说,对于倒数第二行:

sed -n '$!p' file  |  sed -n '$p'

更一般地说,脚本可以迭代sed -n '$!p'以从文件末尾“倒数”。


好吧,答案是:

是否可以在 sed 地址中进行简单的算术运算?

从修辞上讲,它取决于一个人的能力、愿望和愿望以及对实用性的现实评估。同样,这意味着单个sed调用应该专门用于此任务。但是,是的,这是可能的。

在自动机、形式语言和递归函数理论的研究中打下坚实的基础并没有什么坏处。

如前面的答案所述:不仅可以sed做简单的算术,还可以做任何包括复杂算术的可计算函数。然而,要做到这一点需要实现sed递归函数理论(RFT)的原始递归函数(PRF)(当然可以)。当然,机器架构的有限大小确实限制了没有无限磁带资源的计算,正如图灵机所证明的那样。在任何不希望证明这一点的情况下,可以在sed手册中找到先例。

具体来说,做算术(有限)RPN计算器:
https ://www.gnu.org/software/sed/manual/html_node/Increment-a-number.html#FOOT9

现在,使用这样的工具可以创建一个sed脚本,预先计算算术,然后嵌入到sed脚本中以打印所需的输出。OP 给出了一个简单的演示,指出现在可以使用 RPNsed脚本完成 shell 算术计算。

这简化为一种形式,例如(非常粗略)

sed '/$(sed RPN($= - 3*4) file)/;p;' file

但仍然需要提供sed一个sed'd 脚本。此外,可以说是对使用的bash $()争论不休,但可以说它bash已经被用来执行第一个“sed”,所以没有伤害就没有犯规。

认识到sed实现 PRF 或等效地是图灵完备意味着是的,单次调用sed就足够了。

因此,范式可以做到这一点。

可以加快此任务的一些命令是:

e, e command, r, R, w, W

除了通常的保持和模式缓冲区命令。

这些r, R, w, W命令作为临时缓冲空间特别有利。

e [command]  [3.7 Commands Specific to GNU sed][2]

This command allows one to pipe input from a shell command into
pattern space. Without parameters, the e command executes the 
command that is found in pattern space ... 

更抽象地说,尽管非常不切实际,但完全有可能编写一个sed脚本来执行sed范式本身,即使在地址中也包括算术计算。


一个sed特点。表达式/\n/不会匹配任何地址,并且仅当sed像 'N'ext 或 s/.*/\n/ 这样的命令引入一个时,才会匹配模式空间中的地址。通过以下方式确认:

echo -e '\n\n'  | sed -n '   /\n/    {s//hello/;p}'

echo -e '\n\n'  | sed -n '0,/\n\n\n/ {s//hello/;p}'

输出3 个空行和

echo -e '\n\n'  | sed -n '0,/\n/     {s/.*/hello/;p}'
echo -e '\n\n'  | sed -n '0,/\n\n\n/ {s/.*/hello/;p}'

每个输出3 hello's

hello
hello
hello

虽然这是乖巧的:

echo -e '\n\n'  | sed -n '0,/^$/     {s//hello/;p}'

于 2021-10-23T19:22:45.223 回答