我有一个程序可以打印出以“--”分隔的文本行(“段落”)。例如它可能会打印
--
are you happy
--
I am hungry
are you
--
are(you hungry
too
我想将其通过管道传输到另一个程序(也许是 sed?)并只返回以给定单词开头的段落(例如“are”)。因此,在上述情况下,获取以“are”开头的段落,我会得到
--
are you happy
--
are(you hungry
too
该程序打印出可能非常大量的“段落”,但我预计只有一小部分可以匹配,这就是为什么我希望能够以流式传输方式过滤程序的输出(避免将所有内容写入一个巨大的文件中,并且然后过滤它)。
答案1
AWK
使用 GNU awk 或 mawk:
$ awk '$1~"^"word{printf("--\n%s",$0)}' word='are' RS='--\n' infile
--
are you happy
--
are(you hungry
too
这将变量 word 设置为要在记录开头匹配的单词,并将 RS(记录分隔符)设置为“--”,后跟新行\n
。然后,对于以匹配 ( ) 的单词开头的任何记录,$1~"^"word
打印格式化记录。格式是以“--”开头,并带有一个新行,其中包含找到的确切记录。
GREP
使用(GNU 选项-z
)grep:
grep -Pz -- '--\nare(?:[^\n]*\n)+?(?=--|\Z)' infile
grep -Pz -- '(?s)--\nare.*?(?=\n--|\Z)\n' infile
grep -Pz -- '(?s)--\nare(?:(?!\n--).)*\n' infile
描述 对于以下描述,PCRE 选项(?x)
用于添加(大量)与实际(工作)正则表达式内联的解释注释(和空格)。如果注释(和大多数空格)(直到下一个换行符)被删除,则生成的字符串仍然是相同的正则表达式。这允许在工作代码中详细描述正则表达式。这使得代码维护变得更加容易。
选项 1 正则表达式 (?x)--\nare(?:[^\n]*\n)+?(?=--|\Z)
(?x) # match the remainder of the pattern with the following
# effective flags: x
# x modifier: extended. Spaces and text after a #
# in the pattern are ignored
-- # matches the characters -- literally (case sensitive)
\n # matches a line-feed (newline) character (ASCII 10)
are # matches the characters are literally (case sensitive)
(?: # Non-Capturing Group (?:[^\n]*\n)+?
[^\n] # matches non-newline characters
* # Quantifier — Matches between zero and unlimited times, as
# many times as possible, giving back as needed (greedy)
\n # matches a line-feed (newline) character (ASCII 10)
) # Close the Non-Capturing Group
+? # Quantifier — Matches between one and unlimited times, as
# few times as possible, expanding as needed (lazy)
# A repeated capturing group will only capture the last iteration.
# Put a capturing group around the repeated group to capture all
# iterations or use a non-capturing group instead if you're not
# interested in the data
(?= # Positive Lookahead (?=--|\Z)
# Assert that the Regex below matches
# 1st Alternative --
-- # matches the characters -- literally (case sensitive)
| # 2nd Alternative \Z
\Z # \Z asserts position at the end of the string, or before
# the line terminator right at the end of the
# string (if any)
) # Closing the lookahead.
选项 2 正则表达式 (?sx)--\nare.*?(?=\n--|\Z)\n
(?sx) # match the remainder of the pattern with the following eff. flags: sx
# s modifier: single line. Dot matches newline characters
# x modifier: extended. Spaces and text after a # in
# the pattern are ignored
-- # matches the characters -- literally (case sensitive)
\n # matches a line-feed (newline) character (ASCII 10)
are # matches the characters are literally (case sensitive)
.*? # matches any character
# Quantifier — Matches between zero and unlimited times,
# as few times as possible, expanding as needed (lazy).
(?= # Positive Lookahead (?=\n--|\Z)
# Assert that the Regex below matches
# 1st Alternative \n--
\n # matches a line-feed (newline) character (ASCII 10)
-- # matches the characters -- literally.
| # 2nd Alternative \Z
\Z # \Z asserts position at the end of the string, or
# before the line terminator right at
# the end of the string (if any)
) # Close the lookahead parenthesis.
\n # matches a line-feed (newline) character (ASCII 10)
选项 3 正则表达式 (?xs)--\nare(?:(?!\n--).)*\n
(?xs) # match the remainder of the pattern with the following eff. flags: xs
# modifier x : extended. Spaces and text after a # in are ignored
# modifier s : single line. Dot matches newline characters
-- # matches the characters -- literally (case sensitive)
\n # matches a line-feed (newline) character (ASCII 10)
are # matches the characters are literally (case sensitive)
(?: # Non-capturing group (?:(?!\n--).)
(?! # Negative Lookahead (?!\n--)
# Assert that the Regex below does not match
\n # matches a line-feed (newline) character (ASCII 10)
-- # matches the characters -- literally
) # Close Negative lookahead
. # matches any character
) # Close the Non-Capturing group.
* # Quantifier — Matches between zero and unlimited times, as many
# times as possible, giving back as needed (greedy)
\n # matches a line-feed (newline) character (ASCII 10)
sed
$ sed -nEe 'bend
:start ;N;/^--\nare/!b
:loop ;/^--$/!{p;n;bloop}
:end ;/^--$/bstart' infile
答案2
使用 GNUawk
或mawk
:
$ awk -v word="are" -v RS='--\n' -v ORS='--\n' '$1 ~ "^" word "[[:punct:]]?"' file
are you happy
--
are(you hungry
too
--
这将输入和输出的记录分隔符设置为--
后跟换行符。每个段落的第一个单词可以在 中找到$1
。我们将其与给定的单词(可能后跟标点符号)进行匹配。如果它们匹配,则打印该段落。
请注意,输出中的段落标记将放置在每个段落的末尾而不是开头,因为我们用来ORS
输出它们。
使用sed
脚本:
:top
/^--/!d; # This is not a new paragraph, delete
N; # Append next line
/^--\nare[[:punct:]]?/!d; # This is not a paragraph we want, delete
:record
n; # Output line, get next
/^--/!brecord; # Not yet done with this record, branch to :record
btop; # Branch to :top
跑步:
$ sed -E -f script.sed file
--
are you happy
--
are(you hungry
too
或者,作为使用 shell 变量的单行代码$word
:
sed -E -e ':t;/^--/!d;N;' \
-e "/^--\n$word[[:punct:]]?/!d" \
-e ':r;n;/^--/!br;bt' file
答案3
我知道,这是一个老问题,但是看到所有这些循环、分支和模式杂耍,当一个简单的
sed '/^--$/!{H;$!d;};x;/^--\nare/!d'
以自然的方式做同样的事情。
sed
是一个逐行流编辑器;因此,如果您需要多行内容,请H
在段落标记 ( ^--$
) ex
更改缓冲区上收集保留空间中的这些行,并测试是否打印该段落(^--\nare
意味着一行--
后面跟着一行以 开头的行are
)。已经x
用段落标记预加载了保留空间。
你不需要带有狂野扩展的 GNU 工具,你不需要编程技能,只需要参与其中sed
。
答案4
看了你的问题我也有这样的感觉应该可以使用grep
+来解决它PCRE。
- #1 方法解决了这个问题,感谢 @issac 的帮助。
- 方法 #2 显示了如何使用内联修饰符 (
(?s)
) 和前瞻 (?!...
)。 - 我最初的解决方案(#3)在大多数情况下都运行良好,除了我在下面部分中突出显示的类型。
grep 方法 #1
$ grep -Pzo -- '--\nare([^\n]*\n)+?(?=--|\Z)' afile
怎么运行的
grep 开关-P
- PCRE 扩展已启用-z
- 将输入视为多行,使用 NUL 代替\n
(换行符)-o
- 只显示匹配项
--\nare([^\n]*\n)+?(?=--|\Z)
- 匹配双破折号,后跟一个
are
,然后是零个或多个非换行符的延续 - 或换行符。 - 将
+?
匹配 1 个或多个,但不是贪婪的,因此不会积极地继续。 - 最后,
(?=--|\Z)
块末尾的守卫寻找下一个双破折号--
或文件末尾(\Z
)。
- 匹配双破折号,后跟一个
grep 方法 #2
此方法使用 DOTALL 内联修饰符来.
匹配换行符 (`n`)。
$ grep -Pzo -- '(?s)--\nare((?!\n--).)+\n' afile
怎么运行的
grep 开关-P
- PCRE 扩展已启用-z
- 将输入视为多行,使用 NUL 代替\n
(换行符)-o
- 只显示匹配项
(?s)
- 内联修饰符 DOTALL - 所有点都匹配换行符--\nare
- 匹配换行符后跟are
((?!\n--).)+\n
.
-只要向前查找(?!\n--)
不遇到\n--
.就匹配字符。整个匹配块需要至少有一个或多个 (+
) 并后跟换行符\n
。
grep 方法#3(原始)
这是一个grep
利用 PCRE 扩展 ( -P
) 的解决方案。此方法适用于提供的所有示例,但对于如下示例会失败:
--
are
some-other-dasher
但在大多数情况下,我可以想象必须应对。
$ grep -Pzo -- '--\nare[^\r\n]+[^-]+' afile
--
are you happy
--
are(you hungry
too
怎么运行的
grep 开关-P
- PCRE 扩展已启用-z
- 将输入视为多行,使用 NUL 代替\n
(换行符)-o
- 只显示匹配项
'--\nare[^\r\n]+[^-]+'
- 匹配双破折号后跟换行符和单词
are
。 - 然后它将继续打印该行的其余部分,
are
直到遇到换行符。 - 然后它会打印字符,直到遇到一系列破折号。
- 匹配双破折号后跟换行符和单词