Что такое NR и FNR и что означает «NR == FNR»?

85

Я изучаю сравнение файлов, используя awk.

Я нашел синтаксис, как показано ниже,

awk 'NR==FNR{a[$1];next}$1 in a{print $1}' file1 file2

Я не мог понять, какое NR==FNRв этом значение? Если я попробую, FNR==NRто получу такой же результат?

Что именно он делает?

Амит
источник
20
Вы бы удивились, если бы a==bи b==aполучили такой же результат?
Эд Мортон
5
См. Two-file ProcessingНа backreference.org/2010/02/10/idiomatic-awk
Этан Рейснер

Ответы:

93

В awk FNRотносится к номеру записи (обычно номеру строки) в текущем файле и NRотносится к общему номеру записи. Оператор ==- это оператор сравнения, который возвращает истину, если два окружающих операнда равны.

Это означает, что условие NR==FNRистинно только для первого файла, так как FNRсбрасывается обратно на 1 для первой строки каждого файла, ноNR продолжает увеличиваться.

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

Условие FNR==NRсравнивает те же два операнда, что и NR==FNR, поэтому оно ведет себя одинаково.

Том Фенек
источник
3
"=" иногда используется для проверки равенства, а иногда для выполнения присваивания. FNR == NR отличалось бы от NR == FNR, если бы для присвоения использовался двойной знак равенства. Так что для кого-то, кто незнаком с awk, например, спрашивающего, будет разумным спросить, одинаковы ли они.
Тодд Уолтон
@ToddWalton Хорошее замечание! Другой пример: a='3x'; if [[ $a == 3* ]]; then echo yes; fiи вы не можете переключаться с обеих сторон ==.
Уолтер А.
@WalterA да, это правда (по крайней мере, в Bash). Вы предлагаете улучшить мой ответ?
Том Фенек
1
Нет, твой ответ хорош. Мне очень нравится видеть, что и наши ответы нравятся сообществу. Мы используем разные стили, и оба считаются очень полезными. Я только что проголосовал за вас, так что на данный момент у нас одинаковое количество голосов.
Walter A
72

Ищите ключи (первое слово строки) в файле2, которые также находятся в файле1.
Шаг 1: заполните массив a первыми словами файла 1:

awk '{a[$1];}' file1

Шаг 2: Заполните массив a и проигнорируйте файл 2 в той же команде. Для этого проверьте общее количество записей до этого момента с номером текущего входного файла.

awk 'NR==FNR{a[$1]}' file1 file2

Шаг 3. Игнорируйте действия, которые могут произойти после }анализа файла 1.

awk 'NR==FNR{a[$1];next}' file1 file2 

Шаг 4: распечатайте ключ файла2, если он найден в массиве a

awk 'NR==FNR{a[$1];next} $1 in a{print $1}' file1 file2
Уолтер А
источник
4
Блестящее снятие этой однострочной статьи. Нужна ли точка с запятой на шаге 1?
Томаш Гандор
2
@TomaszGandor Точка с запятой не нужна на шаге 1. Я мог бы добавить ее на шаге 3, но ;nextэто странное дополнение (например, добавление nextточки с запятой на шаге 3). Вы можете протестировать шаг 1 с помощью awk '{a[$1]} END { for (k in a) { print "a[k]=" k } }' file1.
Уолтер А.
45

Посмотрите NRи FNRв руководстве по AWK , а затем спросите себя , что это условие , при котором NR==FNRв следующем примере:

$ cat file1
a
b
c

$ cat file2
d
e

$ awk '{print FILENAME, NR, FNR, $0}' file1 file2
file1 1 1 a
file1 2 2 b
file1 3 3 c
file2 4 1 d
file2 5 2 e
Эд Мортон
источник
можно ли еще распечатать номер обрабатываемого файла? есть ли для этого встроенная переменная? (Я знаю, что мы можем создать для этого переменную и увеличивать ее каждый раз, когда NR равен единице)
Лео
В GNU awk эта переменная есть ARGIND, иначе вы можете это сделать FNR==1{ print ++file_nr }.
Эд Мортон
Если позволите, ответить на вопрос другим вопросом не так эффективно;)
Флориан Кастелен,
Я не задавал вопроса, я показал, как получить ответ на вопрос ОП.
Эд Мортон
20

Есть awk встроенные переменные.

NR - Дает общее количество обработанных записей.

FNR - Дает общее количество записей для каждого входного файла.

сидел
источник
15

Предполагая, что у вас есть файлы a.txt и b.txt с

cat a.txt
a
b
c
d
1
3
5
cat b.txt
a
1
2
6
7

Имейте в виду, что NR и FNR - это встроенные переменные awk. NR - Общее количество обработанных записей. (в данном случае как в a.txt, так и в b.txt) FNR - дает общее количество записей для каждого входного файла (записи в a.txt или b.txt)

awk 'NR==FNR{a[$0];}{if($0 in a)print FILENAME " " NR " " FNR " " $0}' a.txt b.txt
a.txt 1 1 a
a.txt 2 2 b
a.txt 3 3 c
a.txt 4 4 d
a.txt 5 5 1
a.txt 6 6 3
a.txt 7 7 5
b.txt 8 1 a
b.txt 9 2 1

позволяет добавить «следующий», чтобы пропустить первое совпадение с NR == FNR

в b.txt и в a.txt

awk 'NR==FNR{a[$0];next}{if($0 in a)print FILENAME " " NR " " FNR " " $0}' a.txt b.txt
b.txt 8 1 a
b.txt 9 2 1

в b.txt, но не в a.txt

 awk 'NR==FNR{a[$0];next}{if(!($0 in a))print FILENAME " " NR " " FNR " " $0}' a.txt b.txt
b.txt 10 3 2
b.txt 11 4 6
b.txt 12 5 7

awk 'NR==FNR{a[$0];next}!($0 in a)' a.txt b.txt
2
6
7
Дон Кеплер Брайан Серемба
источник