Що таке 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
Ні, ваша відповідь чудова. Мені дуже подобається бачити, що громаді так само подобаються наші відповіді. Ми використовуємо різні стилі, і обидва вважаються дуже корисними. Я щойно дав вам голос за, тому на даний момент ми маємо однакову кількість голосів.
Вальтер,

73

Шукайте ключі (перше слово рядка) у файлі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: ключ друку file2, коли він знайдений у масиві 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 дорівнює одиниці)
LE

У GNU awk ця змінна є ARGIND, інакше ви можете це зробити FNR==1{ print ++file_nr }.
Ед Мортон,

Якщо можна, відповісти на запитання іншим питанням не настільки ефективно;)
Флоріан Кастелен

Я не задавав питання, я показував, як отримати відповідь на питання ОП.
Ед Мортон,

19

Є 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 - це вбудовані змінні. 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.