sed打印第n个“ a”至第m个“ b”

时间:2018-11-29 07:28:05

标签: bash shell unix sed

有没有办法仅使用cat和sed打印“ 4”的第4个匹配项到“ 8”的第8个匹配项?

我可以使用=,N,p,q,s作为提示,但不确定如何使用这些

例如,

$ cat foo
1
2
3
4
5-
alkjvearv four
four
asdfasd four 
----fourth asdf
qlvkjqrvlj eight
5+
6
7
8
9
eight eight eight
10
11
12 eight
13 eight
eight qorivjqoerijv
----this is eighth
eight
15

$ cat foo | sed ~
----fourth asdf
qlvkjqrvlj eight
5+
6
7
8
9
eight eight eight
10
11
12 eight
13 eight
eight qorivjqoerijv
----this is eighth

据我所知

$ cat foo | sed -n '/four/,/eight/p'
alkjvearv four
four
asdfasd four 
----fourth asdf
qlvkjqrvlj eight

2 个答案:

答案 0 :(得分:2)

由于您的问题不太精确,因此不确定它是否与您的需求完全匹配,但这可能会给您一个想法。当然,您不应该使用此解决方案(使用awk)。

字符串uuid1uuid2可以用文件中不存在的任何两个字符串替换。

sed -E -e 'H;1h;$!d;x' \
    -e 's/four/uuid1/' -e 's/four/uuid1/' -e 's/four/uuid1/' \
    -e 's/eight/uuid2/' -e 's/eight/uuid2/' -e 's/eight/uuid2/' -e 's/eight/uuid2/' \
    -e 's/eight/uuid2/' -e 's/eight/uuid2/' -e 's/eight/uuid2/' foo |
sed -n '/four/,/eight/p;/eight/q' |
sed 's/uuid1/four/g;s/uuid2/eight/g'

返回:

----fourth asdf
qlvkjqrvlj eight
5+
6
7
8
9
eight eight eight
10
11
12 eight
13 eight
eight qorivjqoerijv
----this is eighth

我认为是正确的。

这个answer和这个one帮助了我。


命令说明:

sed -E -e 'H;1h;$!d;x'

来自this

  

sed命令H; 1h; $!d; x读取了整个文件。

     

由于上面没有使用任何GNU扩展,因此它应该适用于   BSD(OSX)sed。注意,请注意,这种方法需要   能处理长线。 GNU sed应该没问题。那些使用非GNU的人   sed版本应测试其处理长行的能力。

-e 's/four/uuid1/' -e 's/four/uuid1/' -e 's/four/uuid1/' \
-e 's/eight/uuid2/' -e 's/eight/uuid2/' -e 's/eight/uuid2/' -e 's/eight/uuid2/' \
-e 's/eight/uuid2/' -e 's/eight/uuid2/' -e 's/eight/uuid2/' foo

然后我将前三个four替换为uuid1,并将前七个eight替换为uuid2

正如@JonathanLeffler所说,如果增加替换数量会变得非常难看,但是我没有找到POSIX sed版本的任何解决方法,我使用了此answer中的命令。

  

如果GNU sed不可用,而您想更改前3个   从旧到新的出现,然后使用三个s命令:

     

当k为小数时,此方法效果很好,但缩放至大k时效果很差。

sed -n '/four/,/eight/p;/eight/q'-默认情况下不打印;从第一个four到第一个eight,打印该行;当您找到eight时,请退出。

我首先尝试了此操作:sed -n '/four/,/eight/p',但它可能会在应该打印的部分之后返回行。我使用this answer来解决此问题。

此命令选择第一个four和第一个eight出现之间的行。

sed 's/uuid1/four/g;s/uuid2/eight/g'我将uuid替换为其原始值。即使对于uuid1,我也会这样做,因为第三个four与第四个可能在同一行。

答案 1 :(得分:0)

这可能对您有用(GNU sed):

sed ':a;h;s/four/&/4;tb;$d;N;ba;:b;s/eight/&/8;tc;$d;N;bb;:c;x;s/.*\n//;G;s/$/\n/;s/^\([^\n]*\n\).*\1/\1/;s/\n$//;q' file

此命令使用替换命令来检查在处理输入文件时是否已考虑开始/结束字符串。命令s/someString/&/n本身代替了someString的第n次出现。如果替换成功,替换命令还将内部标志设置为true。 t命令允许sed测试内部标志并跳转到sed命令脚本中的另一点,例如如果最后一次替换成功,ta将跳到sed脚本中发生:a的位置。一旦进行了跳转或调用了新的sed周期,内部标志就会重置为false。此机制用于计算开始/结束条件的任意字符串。满足两个条件并删除起始边界之前的部分后,就可以终止输入文件的处理,并打印或不打印结果。

起始条件之前的文本部分的删除可能导致许多边缘情况。为了简化它们,在最终结果中添加了换行符,然后将其删除。

一种更容易但容易出现的错误解决方案:

sed -z 's/four/\x00&/4;s/eight/\x00&/8' file |
sed '/\x00four/,/\x00eight/!d;s/\x00\(four\)/\1/;s/\x00\(eight\)/\1/'