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

时间:2012-09-08 20:38:18

标签: linux shell sed

是否可以在sed地址中进行简单的算术运算? 从"addresses" manual section来看,答案似乎没有。但也许有一种解决方法?

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

sed -n '$-1 p' file

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

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

是否有更好的"更简洁,更易读的方式来处理带有sed地址的算术?


在一个拖延的严重时刻,我决定写一个小script that quickly changes the xterm colorscheme。我们的想法是,.Xresources文件包含开始标记和结束标记:

...
START_MARKER
...
END_MARKER
...

并且您想要删除标记之间的所有内容,而不是标记本身。再一次,这样做会很棒:

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

......但你不能!

5 个答案:

答案 0 :(得分: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;这只是为了说明

答案 1 :(得分: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命令将另一行读入模式空间。这两行现在都在模式空间中,并将用\n分隔。 sed还具有执行流控制的手段,即标签和条件/无条件分支。 man sed中记录了所有内容,here也是完整的参考示例。在上面的代码中,r是一个标签; $!{..}表示“除了最后一行以外的任何地方,请..; N;br读取另一行,并无条件地再次分支到r。所以使用:r;$!{N;br}您可以阅读所有输入进入模式空间,然后将其作为单行操作,\n分隔输入行。

答案 2 :(得分:1)

这可能适合你(GNU sed);

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

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

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

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

答案 3 :(得分: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

答案 4 :(得分:-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