эмуляция grep -B с кольцевым буфером / awk - PullRequest
3 голосов
/ 05 декабря 2011

Мне нужно извлечь строку над строкой поиска (скажем, 19 строк выше).Обычно, я бы просто пошел с

grep -B 19 $search_string $file | ...further processing

Однако скрипт должен работать и в Solaris, где grep не предоставляет опцию -B .Часто я могу использовать awk '/begin/,/end/', чтобы напечатать несколько строк, если я знаю предыдущие строки.В этой конкретной ситуации это невозможно.Я попробовал следующее:

1) Решение кольцевого буфера.

#!/bin/bash
g_a_buffer=( 0 )
g_i_buffer_index=1
while read line
        do
        g_a_buffer[$((g_i_buffer_index % 20))]=$line
        echo $line|grep $search_string > /dev/null
        [ $? -eq 0 ] && echo ${g_a_buffer[$(( (g_i_buffer_index + 2) % 20))]}
        let "g_i_buffer_index += 1"
        done < $file_name

Это очень медленно .Для ~ 40 тыс. Строк требуется 1 м37 с (против 0,005 с grep)

2) Решение Awk.Я должен прямо сказать, что я начинающий экстремал в awk, редко выходя за рамки awk '{print $ 1}'. Следующая строка не работает , но дает вам представление о том, чего я пытаюсь достичь:

awk '/mySearchString/ {print NR-19}' filename.txt 

0,118 для выполнения, скорость хорошая!Но все, что я получаю, это номер строки - 19. Мне нужна распечатка строки, расположенной в (строка - 19).После некоторого поиска, я все еще не мог найти ответ.Я признаю, что это, должно быть, очень простая проблема, но я, кажется, тут попал в стену.

Все, что я нашел до сих пор, - это как напечатать предыдущую строку с помощью awk (что-то вроде буфера в 1 строку)или массивные реализации с кольцевым буфером, но в awk.Есть ли более элегантный способ сделать это?

Спасибо за помощь!

Ответы [ 5 ]

3 голосов
/ 05 декабря 2011

Вот решение, которое требует двух проходов через файл, поэтому не является оптимальным, но на практике вполне может работать разумно.(Протестировано на GNU awk, но нет очевидной причины, по которой он не будет работать на Solaris).

awk "$(awk '/mySearchString/ { print "NR==" NR-19 }' myInputFile.txt)" myInputFile.txt

Поскольку для этого требуется два прохода, если вы передаете входные данные из другого места, вам нужно будет сохранить их ввременный файл где-то.

В качестве альтернативы, если вы знаете, что строка поиска будет появляться в файле не более одного раза (или, по крайней мере, вы заботитесь только о первом появлении), вы можете объединить awk с head и tail, чтобы извлечьстрока:

awk 'NR==1,/mySearchString/' | tail -n 19 | head -n 1

У меня нет подходящего текстового файла, пригодного для сравнения, но я ожидаю, что он будет намного лучше, чем ваше решение с кольцевым буфером.

2 голосов
/ 05 декабря 2011

Вы, вероятно, можете использовать grep -n (что должно быть там, поскольку POSIX определяет -n), чтобы получить номер строки каждого совпадения.

file="foo"
for line in $(grep -n "pattern" "$file" | cut -d: -f1); do
  end=`expr $line + 1`
  head -n $end "$file" | tail -n 3
done

Это -B 1, но этозвучит так, как будто вы просто хотите n -19, поэтому вы можете сделать:

  target=`expr $line - 19`
  head -n $target "$file" | tail -n 1 

Не будет так быстро, как grep, и я не справился с возможными перекрытиями в -B 1 case (выводит строки дважды), но должно работать.Оптимизация может быть выполнена с помощью grep -b (для байтового смещения), если у вас есть это.

1 голос
/ 05 декабря 2011
$ cat mySearcher.sh
#!/bin/ksh

awk '{ array[i++]=$0 }
     END {
       maxI=++i
      for (j=0;j<maxI; j++) {
        if (array[j] ~ /'"${1}"'/) {  #searchTarget
           print array[j-19]
        }
      }
     }
   ' "$2"

сделать его исполняемым

$ chmod 755 mySearch.sh

называется

$ mySearcher.sh "search target" file

Должно стать хорошим началом для решения вашей проблемы

Вы читаете все своиданные в массив awk (могут быть проблемы с файлами-монстрами), затем в блоке END проведите цикл по массиву, сопоставляя каждую запись с вашей целью поиска '

Это не очень хорошо подходит для случая, когда вашsearchTarget перед строкой 19. Вы также можете изменить этот сценарий, чтобы использовать ту же технику, что и $ 1, для цели поиска, чтобы сделать число «оглядки назад» параметром.

Надеюсь, это поможет.

0 голосов
/ 14 января 2012

Ты почти понял!«Правильный» AWK ответ:

awk '$NF ~ "regex" {print $(NF-1)}' input_file
0 голосов
/ 07 декабря 2011

Это может работать для вас:

sed -n ':a;s/\n/&/19;tc;:b;$q;N;ba;:c;/\nPATTERN$/{h;x;s/\n.*//p;x};s/^[^\n]*\n//;ta'

Другой дешевый и неприятный (не обрабатывает перекрытия):

tac | sed -n '/PATTERN/,+19{h;d};x;/^$/!{p;s/.*//};x' | tac

Для обоих, вероятно, требуется GNU sed

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...