Выход из оператора AWK после печати блока текста - PullRequest
1 голос
/ 28 июня 2019

Моя проблема в том, что у меня очень большая база данных (10 ГБ), и я хочу сэкономить как можно больше времени, просматривая ее.У меня есть оператор awk, который выполняет поиск в базе данных и, в зависимости от шаблона, записывает данные в другой файл.
У меня есть входной файл, который будет введен в мой скрипт в качестве переменной аргумента терминала.Внутри него есть несколько строк данных, которые будут использоваться в качестве шаблона для оператора * 1003. *
В базе данных все строки, соответствующие шаблону, отсортированы рядом друг с другом, поэтому, по сути, после печати,нет необходимости искать дальше в базе данных, потому что все уже найдено.Как только awk находит первую строку сопоставления с образцом, все остальные строки сопоставления с образцом располагаются последовательно после нее.

Эту проблему трудно объяснить только словами, поэтому я создал несколько примеров того, что мойфайлы, код и база данных выглядят и работают следующим образом.

Входной файл через терминал выглядит следующим образом:

group_1
group_2
group_3
...

База данных 10 ГБ выглядит следующим образом:

group_1 DATA ...
group_1 DATA ...
group_1 DATA ...
group_2 DATA ...
group_2 DATA ...
group_2 DATA ...
group_2 DATA ...
group_3 DATA ...
group_3 DATA ...
group_3 DATA ...
group_3 DATA ...
...

Код сценария с рассматриваемым оператором awk выглядит следующим образом:

IFS=$'\n'
set -f
for var in $(cat < "$1")
do  
    awk -v seq="$var" '{if (match($1, seq)) {print $0}}' filepath/database  > pattern_matched.file
done

краткое объяснение того, что делает этот код, состоит в том, что он принимает переменную аргумента Terminal, в данном случае имя файлаи открывает его для for loop, чтобы начать цикл.например, шаблон group_1 помещается в var, и начинается поиск в базе данных.Если первый столбец соответствует шаблону, он сохраняет строку в файле pattern_matched.file file.

В настоящее время он просматривает все данные объемом 10 ГБ и печатает данные в файл, как и предполагалось, но тратит впустуюмного времени.После печати строк, соответствующих шаблону, я хочу остановить awk от продолжения поиска в базе данных и перейти к следующему шаблону из входного файла.Примером поведения для group_2 будет awk, проверяющий первые 3 строки базы данных и обнаруживающий, что ни одна из строк не имеет соответствующего шаблона.Однако строка 4 содержит шаблон, поэтому печатает строку и последующие строки, соответствующие шаблону, после него.Когда awk достигает строки 8, он выходит из оператора awk, и for loop может затем переходить к следующему шаблону для поиска, group_3.

awk '{print $0; exit}' filename

Что-то подобное происходитне работает, так как он печатает только первый экземпляр и выходит из строя, я хочу что-то, что может напечатать все совпадения, и как только он найдет следующее совпадение не по шаблону, он выйдет из строя.

Заранее спасибо.

ОБНОВЛЕНИЕ: текущая проблема заключается в том, что приведенное ниже решение имеет логический смысл.Если он входит в оператор if, он выводит строку в файл и переходит к следующей строке.Если строка не совпадает, она вводит оператор else-if и завершает работу awk.Это имеет большой смысл для меня, но по какой-то причине, когда переменная flag была установлена ​​в 1 оператором if для первой совпавшей строки, она входит в оператор else-if.Поскольку условие else-if оценивается как true, оно завершается еще до сканирования следующей строки.Я подтвердил это поведение с помощью операторов print везде в операторе awk.Это мой код с оператором печати:

awk -v seq="$seqid" '{if(match($1, seq)) {print "matched" ; print $1 ; flag=1} else if (flag) {print "not matched" ; exit}}'

, который выводит это: странное поведение

Ответы [ 4 ]

1 голос
/ 28 июня 2019

Код вашей оболочки:

for var in $(cat < "$1")
do  
    awk 'script' filepath/database  > pattern_matched.file
done

использует анти-шаблон для чтения входного файла, хранящегося в $1, см. http://mywiki.wooledge.org/BashFAQ/001, и будет перезаписывать pattern_matched.file на каждой итерациипетля.Вы должны, я подозреваю, записать это как:

while IFS= read -r var
do  
    awk 'script' filepath/database  
done < "$1" > pattern_matched.file

Ваш код awk:

awk -v seq="$var" '{if (match($1, seq)) {print $0}}'

использует match() без необходимости, так как вы просто хотите сделать сравнение регулярных выражений и нене использует переменные, соответствующие match (), чтобы помочь вам изолировать совпадающую строку (RSTART / RLENGTH), и в нем используется условие нулевого значения по умолчанию, а затем помещается реальное условие в пространство действий, а затем жестко кодируется действие по умолчанию печатитекущая запись.Это эквивалентно просто:

awk -v seq="$var" '$1 ~ seq'

, но я не уверен, что вам действительно нужно сравнение с регулярным выражением - учитывая ваш пример, вы должны вместо этого выполнить сравнение строк:

awk -v seq="$var" '$1 == seq'

Учитывая вашеопубликованный пример может вводить в заблуждение, вы просто выберете, какой из них подходит, исходя из того, хотите ли вы регулярное выражение или строку и частичное или полное совпадение для $ 1:

awk -v seq="$var" '$1 == seq'              # full string
awk -v seq="$var" 'index($1,seq)'          # partial string
awk -v seq="$var" '$1 ~ ("^"seq"$")'       # full regexp
awk -v seq="$var" '$1 ~ seq'               # partial regexp

Допустим, мы идем с этой первой полной строкойmatch match, затем выйти, как только будет обработан соответствующий $ 1, будет:

awk -v seq="$var" '$1 == seq{print; f=1; next} f{exit}'

, что сделает ваш полный код:

while IFS= read -r var
do  
    awk -v seq="$var" '$1 == seq{print; f=1; next} f{exit}'  filepath/database  
done < "$1" > pattern_matched.file

НО Я сомневаюсь, что вам нужен цикл оболочкивообще, и вы могли бы просто сделать это вместо этого:

awk 'NR==FNR{seqs[$1]; next} $1 in seqs' "$1" filepath/database > pattern_matched.file

или какой-либо другой вариант, который просто имеет awk (или, может быть, просто join), чтобы прочитать входные файлы один раз.Вы можете выполнить вышеупомянутый выход после того, как все seqs[] были обработаны:

awk '
    NR==FNR { seqs[$1]; numSeqs++; next }
    $1 in seqs { print; if ($1 !== prev) numSeqs--; prev = $1; next }
    numSeqs == -1 { exit }
' "$1" filepath/database > pattern_matched.file

или подобным.

1 голос
/ 28 июня 2019

Не могли бы вы просто прочитать во входном файле (input_file) в awk:

$ cat input_file
group_1
group_3

Сценарий Awk:

$ awk 'NR==FNR{a[$0];next} $1 in a' input_file database
group_1 DATA ...
group_1 DATA ...
group_1 DATA ...
group_3 DATA ...
group_3 DATA ...
group_3 DATA ...
group_3 DATA ...
0 голосов
/ 28 июня 2019

Поскольку мы на самом деле не знаем, что вы собираетесь делать с вашей программой, я просто дам вам решение awk:

awk -v seq="$var" '($1!=seq) { if(p) exit; next }($1==seq){p=1}p'

Используется флаг p для проверки того, соответствует ли он уже последовательности seq. Простое условие if определяет, следует ли ему выйти из awk или перейти к следующей записи. Выход выполняется после того, как seq найден, переход к следующей записи выполняется раньше.

Однако, поскольку вы помещаете это в цикл, он будет читать файл снова и снова и снова. Если вы хотите сделать выбор, вы можете использовать решение Джеймс Браун

0 голосов
/ 28 июня 2019

Я думаю, что это должно сработать:

awk -v seq="$var" '{if (match($1, seq)) {print $0; found=1} else if (found) { exit }}'

Аналогично ответу Дэвида К. Ранкина, но нет необходимости передавать аргумент rd=0 в awk, поскольку в awk любая неинициализированная переменная инициализируется равной нулю при ее первом использовании.

...