使用sed删除两个匹配模式之间的所有行


76

我有一个类似的文件:

# ID 1
blah blah
blah blah
$ description 1
blah blah
# ID 2
blah
$ description 2
blah blah
blah blah

如何使用sed命令删除#和之间的所有$行?因此结果将变为:

# ID 1
$ description 1
blah blah
# ID 2
$ description 2
blah blah
blah blah

您能否也请给一个解释?

Answers:


72

使用此sed命令可以实现以下目的:

sed '/^#/,/^\$/{/^#/!{/^\$/!d}}' file.txt

Mac用户(为防止extra characters at the end of d command错误)需要在方括号前添加分号

sed '/^#/,/^\$/{/^#/!{/^\$/!d;};}' file.txt

输出值

# ID 1
$ description 1
blah blah
# ID 2
$ description 2
blah blah
blah blah

说明:

  • /^#/,/^\$/将匹配#以开头的行与以开头的行之间的所有文本$^用于换行符的开始。$是一个特殊字符,因此需要转义。
  • /^#/! 意思是如果行首不是 #
  • /^$/! 意思是如果行首不是 $
  • d 意味着删除

因此,总的来说,它是首先匹配从^#到的所有行,^\$然后从那些匹配的行中查找不匹配 ^#不匹配的行 ^\$,并使用删除它们d


14
对于Mac用户:为防止extra characters at the end of d command错误,您需要在sed '/^#/,/^\$/{/^#/!{/^\$/!d;};}' file.txt

@sleport的解决方案更为简洁,因此被否决了。
neilm

2
那是拒绝投票的原因吗?如果您喜欢答案,请投票。Downvote通常用于某些不能解决OP问题的答案,并且由于OP接受了此答案,因此意味着它适用于OP。是不是
anubhava

如果要包括要删除的#和$行,应该怎么做?如果您想在行尾找到$,可以执行$ \ $,
Timo

然后使用:sed '/^#/,/^\$/d' file
anubhava

48
$ cat test
1
start
2
end
3
$ sed -n '1,/start/p;/end/,$p' test
1
start
end
3
$ sed '/start/,/end/d' test
1
3

2
在300mb文件上运行的速度令人印象深刻。我正在谈论SSD的瞬间。
雷·福斯

因为我不熟悉sed语法,所以我有点困惑。尚不清楚第一和第二sed命令没有依赖关系-即两者之间的区别在于您是否要保留匹配令牌。在测试之前,我假设第一个命令删除了令牌之间的所有内容,第二个命令删除了令牌本身。如果您要在令牌之间剥离大块,则只需使用第二条命令。
lukevp

不知道为什么,但是'1,/start/p;/end/,$p'完全依赖我的工作,却完全搞砸了我的工作流程。它对我根本不起作用。
Akito

github.com/theAkito/akito-libbash/blob/…是有问题的行。我错过了什么?因为在我看来,它就像您在答案@Lri中显示的一样。
Akito

1
实际有效的解决方案如下:sed '/PATTERN-1/,/PATTERN-2/{//!d}' input.txt
Akito

16

通常,如果您有一个文件,其内容格式为abcde,其中节a在模式b之前,节c在模式d之前,然后节e在后面,并且应用以下sed命令,您将获得以下结果。

在此演示中,输出由表示=> abcde,其中字母表示输出中将包含哪些部分。因此,ae仅显示部分ae的输出,ace将是部分ace等。

请注意,如果在输出中出现b或,则d显示的是这些模式(即,将它们视为输出中的部分)。

也不要将/d/模式与命令混淆d。在这些演示中,命令始终位于末尾。模式始终在之间//

  • sed -n -e '/b/,/d/!p' abcde => AE
  • sed -n -e '/b/,/d/p' abcde => BCD
  • sed -n -e '/b/,/d/{//!p}' abcde => c
  • sed -n -e '/b/,/d/{//p}' abcde => bd
  • sed -e '/b/,/d/!d' abcde => BCD
  • sed -e '/b/,/d/d' abcde => AE
  • sed -e '/b/,/d/{//!d}' abcde =>阿卜德
  • sed -e '/b/,/d/{//d}' abcde =>王牌

10

sed的另一种方法:

sed '/^#/,/^\$/{//!d;};' file
  • /^#/,/^\$/:从#以第一个开始的行到下一个以$
  • //!d:删除除与地址模式匹配的行以外的所有行

1
您如何将这些模式包括在内?
qodeninja

2
试试这个:sed '/^#/,/^\$/d;' file
SLePort '17

4

很久以前我做了这样的事情,它是这样的:

sed -n -e "1,/# ID 1/ p" -e "/\$ description 1/,$ p"

就像这样:

  • -n 抑制所有输出
  • -e "1,/# ID 1/ p" 从第一行开始执行直到您的模式和p(打印)
  • -e "/\$ description 1/,$ p" 从第二个模式开始执行,直到结尾并按下p(打印)。

字符串中的一些转义符可能是错误的,因此请仔细检查。


0

下面的示例删除了“ if”“ end if”之间的行

扫描所有文件,并删除两个匹配模式之间的线(包括它们)。

IFS='
'
PATTERN_1="^if"
PATTERN_2="end if"

# Search for the 1st pattern in all files under the current directory.
GREP_RESULTS=(`grep -nRi "$PATTERN_1" .`)

# Go through each result
for line in "${GREP_RESULTS[@]}"; do

   # Save the file and line number where the match was found.
   FILE=${line%%:*}
   START_LINE=`echo "$line" | cut -f2 -d:`

   # Search on the same file for a match of the 2nd pattern. The search 
   # starts from the line where the 1st pattern was matched.
   GREP_RESULT=(`tail -n +${START_LINE} $FILE | grep -in "$PATTERN_2" | head -n1`)
   END_LINE="$(( $START_LINE + `echo "$GREP_RESULT" | cut -f1 -d:` - 1 ))"

   # Remove lines between first and second match from file
   sed -e "${START_LINE},${END_LINE}d;" $FILE > $FILE

done
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.