从 Bash 中的特定行反向搜索模式
Reverse Search for Pattern from Specific Line in Bash
假设我有一个很大的 XML 字典,格式如下:
<entry>
<!-- arbitrary amount of lines -->
<head>SomeWord</head>
<!-- arbitrary amount of lines -->
</entry>
假设我知道 SomeWord 在第 3,026,138 行。我想从第 3,026,138 行向后搜索直到 <entry>
,但我不知道 <entry>
和我的目标行之间有多少行。
如果我使用行号而不是模式, 可以正常工作,如下所示
sed '/<entry>/h;//!H;3026138!d;x;q' file
但是,这是一个不太理想的解决方案,因为我认为 sed
是从第 0 行开始扫描并在文件中爬行 300 万行。这似乎很浪费,因为我已经知道我想在文件的哪个区域工作。总而言之,它大约需要半秒钟。
有没有人有解决方案,利用我知道行号这一事实,使用每个人都已经拥有的正常 Unix/sh 程序(例如 grep、awk、sed 等) ?
注意:请不要建议我使用 xmllint
之类的东西。它不仅非常慢,而且我还希望这是一个与元格式无关的脚本。
我在这里尝试了以下内容:
- 将 条目 标记行号保存到单独的文件中
- 指定head标签所需的行号
- 执行搜索“它适合哪里”
输入文件:
someline
someline
<entry>
someline
someline
<head>Here</head>
someline
</entry>
someline
<entry>
someline
<head>Another</head>
someline
someline
someline
</entry>
someline
someline
shell 脚本(可以分开以对给定的($1)行号执行搜索。对文件执行多个搜索或以各种方式使用它(通过不同的方法获取所需的标签,然后给出执行搜索的搜索脚本的行号)
# preparation before doing searches
ln=12 # line number with desired <head>
cat input.txt | sed '$a<entry>' | grep -n '^<entry>' | cut -d ':' -f1 > entryl.txt
# doing searches
t=0
for x in $(seq $(cat entryl.txt | wc -l)); do
c=$(cat entryl.txt | head -n $x | tail -n 1)
if test $t -eq 1; then
if test $ln -lt $c; then
echo "<head> tag on line: $ln"
echo "Previous <entry> found at: $p"
echo "Next <entry> found at: $c"
break;
else
p=$c
fi
else
if test $ln -gt $c; then
p=$c; t=1
fi
fi
done
示例输出:
<head> tag on line: 12
Previous <entry> found at: 10
Next <entry> found at: 19
sed
等工具的问题在于,当您想要处理整个文件的一大块时,它们会一次处理一行。输入 ed
。以下打印了第 3026138 行之前找到的带有 <entry>
的第一行到该行之间的所有内容:
echo "3026138;?<entry>?,.p" | ed -s file
(将当前行设置为第3026138行,打印当前行之前第一个匹配<entry>
到当前行的范围。如果要将块保存在另一个文件中,可以使用w foo.txt
而不是 p
).
使用示例文件和第 3 行的示例:
$ echo "3;?<entry>?,.p" | ed -s input.txt
<entry>
<!-- arbitrary amount of lines -->
<head>SomeWord</head>
假设我有一个很大的 XML 字典,格式如下:
<entry>
<!-- arbitrary amount of lines -->
<head>SomeWord</head>
<!-- arbitrary amount of lines -->
</entry>
假设我知道 SomeWord 在第 3,026,138 行。我想从第 3,026,138 行向后搜索直到 <entry>
,但我不知道 <entry>
和我的目标行之间有多少行。
sed '/<entry>/h;//!H;3026138!d;x;q' file
但是,这是一个不太理想的解决方案,因为我认为 sed
是从第 0 行开始扫描并在文件中爬行 300 万行。这似乎很浪费,因为我已经知道我想在文件的哪个区域工作。总而言之,它大约需要半秒钟。
有没有人有解决方案,利用我知道行号这一事实,使用每个人都已经拥有的正常 Unix/sh 程序(例如 grep、awk、sed 等) ?
注意:请不要建议我使用 xmllint
之类的东西。它不仅非常慢,而且我还希望这是一个与元格式无关的脚本。
我在这里尝试了以下内容:
- 将 条目 标记行号保存到单独的文件中
- 指定head标签所需的行号
- 执行搜索“它适合哪里”
输入文件:
someline
someline
<entry>
someline
someline
<head>Here</head>
someline
</entry>
someline
<entry>
someline
<head>Another</head>
someline
someline
someline
</entry>
someline
someline
shell 脚本(可以分开以对给定的($1)行号执行搜索。对文件执行多个搜索或以各种方式使用它(通过不同的方法获取所需的标签,然后给出执行搜索的搜索脚本的行号)
# preparation before doing searches
ln=12 # line number with desired <head>
cat input.txt | sed '$a<entry>' | grep -n '^<entry>' | cut -d ':' -f1 > entryl.txt
# doing searches
t=0
for x in $(seq $(cat entryl.txt | wc -l)); do
c=$(cat entryl.txt | head -n $x | tail -n 1)
if test $t -eq 1; then
if test $ln -lt $c; then
echo "<head> tag on line: $ln"
echo "Previous <entry> found at: $p"
echo "Next <entry> found at: $c"
break;
else
p=$c
fi
else
if test $ln -gt $c; then
p=$c; t=1
fi
fi
done
示例输出:
<head> tag on line: 12
Previous <entry> found at: 10
Next <entry> found at: 19
sed
等工具的问题在于,当您想要处理整个文件的一大块时,它们会一次处理一行。输入 ed
。以下打印了第 3026138 行之前找到的带有 <entry>
的第一行到该行之间的所有内容:
echo "3026138;?<entry>?,.p" | ed -s file
(将当前行设置为第3026138行,打印当前行之前第一个匹配<entry>
到当前行的范围。如果要将块保存在另一个文件中,可以使用w foo.txt
而不是 p
).
使用示例文件和第 3 行的示例:
$ echo "3;?<entry>?,.p" | ed -s input.txt
<entry>
<!-- arbitrary amount of lines -->
<head>SomeWord</head>