在下面的awk
命令中找到了1.txt
与2.txt
.
awk 'NR==FNR{b[$0]=1;next}!b[$0]' 1.txt 2.txt
Run Code Online (Sandbox Code Playgroud)
需要逐步了解此awk
构造如何找到丢失的行。
该脚本输出第二个文件中没有出现在第一个文件中的任何行。
awk 'NR==FNR { b[$0] = 1; next } !b[$0]' 1.txt 2.txt
Run Code Online (Sandbox Code Playgroud)
该awk
脚本开始与比较NR
来FNR
。 NR
是迄今为止读取的记录(行)总数,包括当前记录。 FNR
是从当前输入文件读取的记录数。如果这两个数字相同,那么我们仍在读取第一个输入文件。请注意,这将打破,如果第一个文件正好是空的NR == FNR
,然后将第二个文件是真实的为好。
如果我们正在读取第一个输入文件(我们假设它是非空的),b[$0] = 1
将使用当前记录的内容作为哈希键并将该键的值 1 存储在数组中b
(数组索引可能是 ) 中的字符串awk
。然后脚本会执行next
,这意味着它会跳回到脚本的开头并读取下一条记录。
如果NR
是不等于FNR
,那么这意味着我们正在阅读的第二两个输入文件,并!b[$0]
与当前输入记录(行)作为钥匙插入阵列的测试b
我们前面填充。如果 1 为当前记录存储在 中b
,那么我们知道这是先前在第一个文件中找到的。该!
否定的考验。
如果测试为真,即如果之前没有在第一个文件中看到第二个文件中的当前行,则执行默认操作。没有相应{...}
块的测试的默认操作是输出当前行(即它的行为就像代码是!b[$0] { print }
)。
由于此awk
脚本将第一个文件中的所有(唯一)行读取到内存中,因此在非常大的文件上运行可能不是一个好主意。
在这些情况下,最好做一些类似的事情
comm -13 <( sort -u file1 ) <( sort -u file2 )
Run Code Online (Sandbox Code Playgroud)
(这需要一个知道进程替换的 shell),或者只是
comm -13 file1 file2
Run Code Online (Sandbox Code Playgroud)
如果文件已经排序。
这不会生成完全相同的输出,因为awk
脚本将输出file2
每次出现多次的任何行,而comm
如果sort -u
在输入上使用上面的命令,则不会。
有关comm
详细信息,请参阅系统手册。
解决评论中的问题:
FNR
是从当前输入文件读取的记录数。NR
并且FNR
不“属于”任何一个文件,它们只是计数器。当FNR
到达文件末尾时,计数器会重置。NR
并FNR
都在从文件中读取一行递增。该next
命令强制跳转到脚本的开头,这也会导致读取下一行。NR
并且FNR
由于读取新行而通过此操作递增。NR != FNR
那么这意味着我们已经通过了第一个文件。FNR
在到达前一个文件的末尾时被重置为零,但NR
只是继续计数。$0
是一个保存当前行的变量。它保存从文件中读取的完整行。$1
,$2
等保存字段当前行为对变量的值分割的IFS
(通常是任何空白)。如果当前行是hello world
,$0
则将具有该值hello world
而$1
具有该值hello
并$2
具有该值world
(因为该行在空间上被拆分)。该脚本仅使用$0
,您可能会将其$0
视为“当前输入行的内容”。b[$0] = 1
是将值分配给数组中的特定位置/索引b
。位置由当前行决定,$0
赋值为1。这使得数组b
就像一个“查找表”;如果b[i]
对于任何特定索引为 1 i
,那么这意味着它在第一个输入文件中被看到。!b[$0]
如果存储在索引处$0
的b
值为零(或未初始化),即如果b[$0]
从未分配值 1,即从第二个文件中读取的行先前未在第一个文件中看到,则为真。由于{...}
本次测试没有对应的动作(无块),所以$0
执行打印的默认动作。这具有打印第二个文件中第一个文件中不存在的每一行的效果。