2015-12-08 1 views
4

Я пытаюсь использовать GAWK из CYGWIN для обработки CSV-файла. Pass 1 находит максимальное значение, а pass 2 печатает записи, соответствующие максимальному значению. Я использую файл .awk в качестве входных данных. Когда я использую текст в руководстве, он совпадает с обоими проходами. Я могу использовать форму IF как обходной путь, но это заставляет меня использовать IF внутри каждого совпадения шаблонов, что является своего рода болью. Любая идея, что я делаю неправильно?Как использовать несколько проходов с gawk?

Вот мой .awk файл:

pass == 1 
{ 
    print "pass1 is", pass; 
}  

pass == 2 
{ 
if(pass == 2) 
    print "pass2 is", pass; 
}  

Вот мой вывод (входной файл просто «привет):

hello 
pass1 is 1 
pass1 is 2 
hello 
pass2 is 2 

Вот моя командная строка:

gawk -F , -f test.awk pass=1 x.txt pass=2 x.txt 

Я d оценить любую помощь.

+0

FYI GNU awk имеет переменную с именем 'ARGIND', которая делает ваши переменные' pass' избыточными. –

ответ

5

An (Г) AWK решение может выглядеть следующим образом:

awk 'FNR == NR{print "1st pass"; next} 
    {print "second pass"}' x.txt x.txt 

(Пожалуйста, замените awk на gawk при необходимости.)
Допустим, вы хотите найти максимальное значение в первом столбце файла x.txt, а затем распечатать все линии, которые имеют это значение в первом столбце, ваша программа может выглядеть следующим образом (спасибо Ed Мортон для некоторых кончике см комментарий):

awk -F"," 'FNR==NR {max = ((FNR==1) || ($1 > max) ? $1 : max); next} 
      $1==max' x.txt x.txt 

выход для x.txt:

6,5 
2,6 
5,7 
6,9 

является

6,5 
6,9 

Как это работает? Переменная NR продолжает увеличиваться с каждой записью, тогда как FNR сбрасывается до 1 при чтении нового файла. Следовательно, FNR==NR применим только для первого обработанного файла.

+0

@MarkSetchell: Вы правы, скобки не нужны. Поэтому я обновил свой ответ. Тем не менее, для таких людей, как я, которые привыкли к Java/C ... скобки, содержащие условие, несколько более знакомы '(условие) {code block}'. –

+2

В этом скрипте нет ничего специфичного для gawk. Чтобы не требовать max, чтобы быть> = 0 и сделать ваш скрипт переносимым для всех awks (некоторые awks будут терпеть неудачу в некоторых ситуациях с неравновешенными тернарными выражениями) и легче читать, измените ваш тест на 'FNR == NR {max = ((FNR == 1) || ($ 1> max)? $ 1: max); Следующий} '. Каждый раз, когда вы делаете вычисление min или max, семя с первым прочитанным значением, не принимайте/семя с некоторым случайным значением, равным нулю. Вы можете и должны удалить '{print $ 0}', поскольку это действие по умолчанию, когда условие истинно. –

+0

@EdMorton: Спасибо за замечания. Я соответствующим образом изменил свой ответ (также даю вам кредит) –

3

Итак ... Ф.Кнорр ответил на ваш вопрос точно и кратко, и он заслуживает большой зеленой галочки. NR==FNR - это именно тот секретный соус, который вы ищете.

Но здесь есть другой подход, на всякий случай многопроходная вещь оказывается проблематичной. (Возможно, вы читаете файл с медленного диска, флешку, по сети, DAT ленты и т.д.)

awk -F, '$1>m{delete l;n=0;m=$1}m==$1{l[++n]=$0}END{for(i=1;i<=n;i++)print l[i]}' inputfile 

Или, разнесены для удобства чтения:

BEGIN { 
    FS="," 
} 

$1 > max { 
    delete list   # empty the array 
    n=0     # reset the array counter 
    max=$1    # set a new max 
} 

max==$1 { 
    list[++n]=$0   # record the line in our array 
} 

END { 
    for(i=1;i<=n;i++) { # print the array in order of found lines. 
    print list[i] 
    } 
} 

С те же входные данные, с которыми тестировался F.Knorr, я получаю одинаковые результаты.

Идея здесь заключается в том, чтобы просмотреть файл в ОДНОМ проходе. Мы записываем каждую строку, которая соответствует нашему максимуму в массиве, и если мы сталкиваемся с значением, превышающим max, мы очищаем массив и начинаем сбор строк заново.

Этот подход является heaver на процессоре и памяти (в зависимости от размера вашего набора данных), но, будучи одним проходом, он, вероятно, будет легче на IO.

+0

Приятный, но по соглашению и как созданный каждой функцией awk, awk-массивы (и строковые позиции символов и номера полей) начинаются с одного, а не 0, поэтому просто подстройте «list [++ n] = $ 0 .... для (i = 1; i

+1

Большое спасибо за указание на это. За все мои годы awk это никогда не приходило мне в голову. Я скорректировал этот ответ, но теперь мне нужно расчесывать небольшую коллекцию других скриптов, чтобы выполнить аналогичные настройки. Whee! :-P – ghoti

+1

@EdMorton - oh, а условие в цикле for в END также необходимо изменить на 'i <= n'. – ghoti

0

Проблема здесь в том, что новые строки относятся к awk.

# This does what I should have done: 
pass==1 {print "pass1 is", pass;} 
pass==2 {if (pass==2) print "pass2 is", pass;} 

# This is the code in my question: 
# When pass == 1, do nothing 
pass==1 
# On every condition, do this 
    {print "pass1 is", pass;} 
# When pass == 2, do nothing 
pass==2 
# On every condition, do this 
    {if (pass==2) print "pass2 is", pass;} 

Использование pass == 1, pass == 2 не так изящно, но оно работает.