我想在文件的多行中搜索特定字符串并获取找到匹配项的行。
但是,我的问题是,该文件包含一个非常长的字符串,而不是单词,我想搜索这个长字符串的子序列。因此,我不能使用 pcregrep,而只能搜索 word1\nword2。因为我实际上想获取找到匹配项的行号,所以我不能只删除所有换行符...
这是我的文件的示例,我只是将匹配的字符串大写,以便您可以找到它:
要搜索的字符串:
gcbcdbfceebcfhfchaaccdgfcegffgedffaeaedcbaedhacebeeebcechbcbfeeccbdhcbfg
搜索文件:
abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde
abcdeabcde***GCBCDBFCEEBCFHFCHAACCDGFCEGFFGEDFFAEAEDC
BAEDHACEBEEEBCECHBCBFEECCBDHCBFG***ggfbhbgcedabceedfa
fbaaechaabdbffbebecebaacfcfcdcggfchddcefbcbdegbbba
你们中有人有一个简单的解决方案吗?
如果手头没有工具可以做到这一点,我会编写一个简短的 python 脚本来完成它,但我想任何 bash 工具都会比这更有效率......
编辑:
非常感谢您的回答,如果知道换行符的位置,它们会非常有效。
但是,很抱歉我的问题不够准确。我的问题是,我不知道文件中的字符串中是否有换行符,甚至多个换行符,而且,我不知道在哪里是的。我通过删除无意中插入的换行符来更正我的搜索字符串。
是否有某种方法允许在字符串的任何位置出现换行符?
答案1
现在我对这个问题有了更好的理解,所以我添加了一个新答案。我只是将其作为一个工作示例发布,但我并不认为这是一个好示例。:)
此外,我明白这个问题似乎是因为担心效率低下而不想使用 Python。所以我明白这种方法不能满足整个要求。:(
#!/usr/bin/env python
import sys
def findall_iter(S, pat):
index = -1
while True:
try:
index = S.index(pat, index+1)
yield index
except ValueError:
raise StopIteration
def findall(S, pat):
return list(findall_iter(S, pat))
# read in arguments
S = open(sys.argv[2]).read()
pattern = sys.argv[1]
# get indices of all newlines
newline_indices = findall(S, '\n')
# get psudo-indices of all pattern matches
pat_indices = findall(S.replace('\n', ''), pattern)
# iterate through each pattern match psudo-index and
# correlate it back to a real line number from the file
line_numbers = []
for pi in pat_indices:
for i, ni in enumerate(newline_indices):
if ni > pi+i:
line = i + 1
if line not in line_numbers:
line_numbers.append(i+1)
break
print '\n'.join(map(str, line_numbers))
优点:
- 如果文件不是太大(<1GB)则所有操作都在内存中执行。
- 使用 str.index 方法查找子字符串,而不是(较慢的)正则表达式匹配
- 比使用正则表达式更清晰
缺点:
- 不适用于处理大文件。
- 创建两个临时字符串来完成这项工作。
- 最后的 for 循环很难理解。
- 是 Python(我个人并不认为这是一个缺点)。
答案2
我会用脚本来做这件事sed
。把它放在一个文件中,然后用来sed -nf
运行它。
:restart
/gcbcdbfceebcfhfchaaccdgfcegffgedffaeaedc$/{
# Found the first part, now discard it
s/^.*$//
# Read a new line into the buffer
N
# Discard the new line inserted by the N operation
s/^\n//
# If next line isn't a match, start over
/^baedhacebeeebcechbcbfeeccbdhcbfg/!b restart
# If it is a match, print the line number
=
}
在 下运行它看起来是这样的bash
。请注意,它打印出匹配的第二行的行号。
bash-4.1$ cat sample.txt
abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde
abcdeabcde***gcbcdbfceebcfhfchaaccdgfcegffgedffaeaedc
baedhacebeeebcechbcbfeeccbdhcbfg***ggfbhbgcedabceedfa
fbaaechaabdbffbebecebaacfcfcdcggfchddcefbcbdegbbba
bash-4.1$
bash-4.1$ cat findmatch.sed
:restart
/gcbcdbfceebcfhfchaaccdgfcegffgedffaeaedc$/{
# Found the first part, now discard it
s/^.*$//
# Read a new line into the buffer
N
# Discard the new line inserted by the N operation
s/^\n//
# If next line isn't a match, start over
/^baedhacebeeebcechbcbfeeccbdhcbfg/!b restart
# If it is a match, print the line number
=
}
bash-4.1$
bash-4.1$ sed -nf findmatch.sed sample.txt
3
bash-4.1$
答案3
我有点困惑您是在什么限制下操作的。但是,如果您需要行号,grep 和 pcregrep 都可以使用 -n 标志将其提供给您。
$ pcregrep -nM "gcbcdbfceebcfhfchaaccdgfcegffgedffaeaedc\nbaedhacebeeebcechbcbfeeccbdhcbfg" | cut -d: -f1
2
baedhacebeeebcechbcbfeeccbdhcbfg***ggfbhbgcedabceedfa
sed -n 'p;N'
pcregrep 仅显示匹配的第一行的行号,显然,如果您只希望输出行号,则必须使用 sed 跳过输出的其他每一行(将上面的内容连接到)。