从 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 之类的东西。它不仅非常慢,而且我还希望这是一个与元格式无关的脚本。

我在这里尝试了以下内容:

  1. 条目 标记行号保存到单独的文件中
  2. 指定head标签所需的行号
  3. 执行搜索“它适合哪里

输入文件:

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>