我需要提取每行中可能出现 1 到 n 次的字符串部分。
例如,这将反映我所需要的:
This [dbo].[something] is a text containing [dbo].[something_else], then okay?
And then, [dbo].[something] may appear just once.
But why, nothing prevents [dbo].[something] from appearing twice as [dbo].[something] here.
And then can be three times, as [dbo].[something] is [dbo].[anything] but [dbo].[elsewhere] here.
[dbo].[otherthing] depicts another scenario with just one and pattern heading line
Or, also [dbo].[ultra] with an arbitrary amount of [dbo].[references] but ending with [dbo].[pattern]
您可能已经注意到,该模式将是\[dbo\]\.\[[^]]+\]
.例如,从上面的文本中,我想要一个结果:
something something_else
something
something something
something anything elsewhere
otherthing
ultra references pattern
然后我可以内联所有内容(或附加到 bash 数组)并过滤重复项,这不应该是问题。我只是很难弄清楚如何在一次扫描中完成这个过滤器。
我在这里得到的结果是仅提取最后一个匹配项(当您习惯了 sed 的“贪婪”模式匹配方法时,很明显为什么会这样):
cat dborefs.txt | sed -E "s/(.*\[dbo\]\.\[([^]]+)\].*)*/\2/g"
something_else
something
something
elsewhere
otherthing
pattern
我可以提取,然后替换模式,使它们不再匹配,然后再次提取,直到不再匹配,但这听起来太麻烦了,考虑到所有 bash 开销;最好能够在一次调用中提取所有内容sed
。我觉得这应该是可能的,只是无法轻易弄清楚如何实现。我认为这可能对其他人有用,因此我觉得在这里分享这个问题可以为社区带来丰硕的成果。
答案1
要获取以换行符分隔的标记字符串列表:
$ grep -o '\[dbo\]\.\[[^]]*\]' file | cut -d . -f 2 | tr -d '[]'
something
something_else
something
something
something
something
anything
elsewhere
otherthing
ultra
references
pattern
第一个grep
仅生成带有[dbo].[word]
.为cut
我们提供了[word]
位,而从其中tr
删除了[
和。]
要获取按它们出现的行分组的标记字符串:
$ sed -e 's/\][^.[]*\[/] [/g' -e 's/^[^[]*//' -e 's/[^]]*$//' -e 's/\[dbo\]\.\[\([^]]*\)\]/\1/g' file
something something_else
something
something something
something anything elsewhere
otherthing
ultra references pattern
这里使用的四个替换是
- 删除
]
和之间[
不是点或 a 的所有内容[
(实际上,用空格替换;这些是最终输出中的空格)。 - 删除第一个之前的所有内容
[
。 - 删除最后一个之后的所有内容
]
。 - 提取剩余内容中标记的单词。
答案2
目前,我可以(希望)比重复调用 sed 更好的方法是用希望不会出现在文件中的占位符“链接”替换。
cat dborefs.txt | sed -E "
s/\[dbo\]\.\[([^]]+)\]/_-\1-_/g;
s/(^|-_)([^_]+|_[^-])*(\$|_-)/ /g;
s/(^ +| +\$)//g"
换句话说:
- 首先我得到所有
[dbo].[<extract>]
并替换为_-<extract>-_
; - 然后将第一个之前
_-
、之间-_
以及_-
最后一个之后的任何文本替换-_
为单个空白字符; - 然后清理每行开头和结尾的空白字符。
这给出了所需的结果,我可以将其全部连接到一个数组中,然后过滤sort
唯一的条目。但我仍然认为应该有一种更好的方法,无需链式sed
命令。
答案3
您可以在 Perl 中更轻松地完成此操作,使用散列(关联数组)来唯一化匹配:
$ perl -nE 'while ($_ =~ /\[dbo\]\.\[(.*?)\]/g) {$h{$1}++} }{ for $k (keys %h) {say $k}' dborefs.txt
otherthing
anything
elsewhere
something
pattern
something_else
ultra
references
通过重复应用该函数,GNU Awk 中可以采用类似的方法match
:
$ gawk '{
while (match($0,/\[dbo\]\.\[([^]]+)\]/,a)) {h[a[1]]++; $0 = substr($0,RSTART+RLENGTH)}
}
END{
for (k in h) print k
}' dborefs.txt
references
elsewhere
something
something_else
pattern
otherthing
anything
ultra
对于其他 Awk 实现,其match
函数不提供捕获组数组,您需要修剪匹配:
while (match($0,/\[dbo\]\.\[([^]]+)\]/)) {h[substr($0,RSTART+7,RLENGTH-8)]++; $0 = substr($0,RSTART+RLENGTH)}
答案4
还有另一种方法,这次使用多个实用程序。管道的 sed 部分提取模式,而 awk 部分对其进行唯一化,同时保留它们首次出现的顺序。
sed -Ee '
/\n/{P;D;}
s/\[dbo]\.\[([^]]+)]/\n\1\n/;D
' dborefs.txt | awk '!a[$0]++'