¿Qué son NR y FNR y qué implica “NR == FNR”?

83

Estoy aprendiendo a comparar archivos usando awk.

Encontré una sintaxis como la siguiente,

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

No pude entender cuál es el significado de NR==FNResto. Si lo intento FNR==NR, ¿también obtengo el mismo resultado?

¿Qué hace exactamente?

Amit
fuente
20
¿Le sorprendería que a==by b==aprodujera el mismo resultado?
Ed Morton
5
Ver Two-file Processingen backreference.org/2010/02/10/idiomatic-awk
Etan Reisner

Respuestas:

91

En awk, se FNRrefiere al número de registro (normalmente el número de línea) en el archivo actual y se NRrefiere al número total de registro. El operador ==es un operador de comparación, que devuelve verdadero cuando los dos operandos circundantes son iguales.

Esto significa que la condición NR==FNRsolo es verdadera para el primer archivo, ya que se FNRrestablece a 1 para la primera línea de cada archivo, pero NRsigue aumentando.

Este patrón se usa generalmente para realizar acciones solo en el primer archivo. El nextinterior del bloque significa que se omiten los comandos adicionales, por lo que solo se ejecutan en archivos distintos del primero.

La condición FNR==NRcompara los mismos dos operandos que NR==FNR, por lo que se comporta de la misma manera.

Tom Fenech
fuente
3
"=" se utiliza a veces para probar la igualdad y, a veces, para realizar una asignación. FNR == NR sería diferente de NR == FNR si se usara el signo doble igual para la asignación. Entonces, para alguien que no esté familiarizado con awk, como este autor de la pregunta, parece razonable preguntar si son lo mismo.
Todd Walton
@ToddWalton ¡Buen punto! Otro ejemplo: a='3x'; if [[ $a == 3* ]]; then echo yes; fiy no puedes cambiar ambos lados de ==.
Walter A
@WalterA sí, eso es cierto (en Bash, al menos). ¿Sugieres alguna mejora a mi respuesta?
Tom Fenech
1
No, tu respuesta está bien. Realmente me gusta ver que a la comunidad le gustan tanto nuestras respuestas. Usamos diferentes estilos y ambos se consideran muy útiles. Te acabo de dar un voto positivo, por lo que en este momento tenemos el mismo número de votos positivos.
Walter A
70

Busque claves (primera palabra de la línea) en el archivo2 que también están en el archivo1.
Paso 1: complete la matriz a con las primeras palabras del archivo 1:

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

Paso 2: Complete la matriz ay ignore el archivo 2 en el mismo comando. Para esto, verifique el número total de registros hasta ahora con el número del archivo de entrada actual.

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

Paso 3: ignore las acciones que puedan surgir después del }análisis del archivo 1

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

Paso 4: imprima la clave del archivo2 cuando se encuentre en la matriz a

awk 'NR==FNR{a[$1];next} $1 in a{print $1}' file1 file2
Walter A
fuente
4
Brillante derribo de este one-liner. ¿Es necesario el punto y coma en el paso 1?
Tomasz Gandor
2
@TomaszGandor El punto y coma no es necesario en el paso 1. Podría haberlo agregado en el paso 3, pero ;nextes una adición extraña (como agregar nexty necesito el punto y coma en el paso 3). Puede probar el paso 1 con awk '{a[$1]} END { for (k in a) { print "a[k]=" k } }' file1.
Walter A
43

Busque NRy FNRen el manual de awk y luego pregúntese cuál es la condición bajo la cual NR==FNRen el siguiente ejemplo:

$ 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
Ed Morton
fuente
¿Es posible también imprimir el número del archivo que se está procesando? ¿Hay una variable incorporada para eso? (Sé que podríamos crear una variable para eso e incrementarla cada vez que NR sea uno)
LEo
En GNU awk esa variable es ARGIND, de lo contrario puedes hacerlo FNR==1{ print ++file_nr }.
Ed Morton
Si puedo, responder una pregunta con otra pregunta no es tan eficiente;)
Florian Castelain
No hice una pregunta, mostré cómo obtener la respuesta a la pregunta de los OP.
Ed Morton
20

Existen awk variables integradas.

NR - Da el número total de registros procesados.

FNR - Da el número total de registros para cada archivo de entrada.

se sentó
fuente
14

Suponiendo que tiene archivos a.txt y b.txt con

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

Tenga en cuenta que NR y FNR son variables integradas de awk. NR: proporciona el número total de registros procesados. (en este caso tanto en a.txt como en b.txt) FNR - Da el número total de registros para cada archivo de entrada (registros en a.txt o 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

vamos a agregar "siguiente" para omitir el primero que coincide con NR == FNR

en b.txt y en 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

en b.txt pero no en 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
Don Kepler Brian Seremba
fuente