需要了解以下 awk 命令以查找文件中丢失的行

Pra*_* BJ 2 awk

在下面的awk命令中找到了1.txt2.txt.

awk 'NR==FNR{b[$0]=1;next}!b[$0]' 1.txt 2.txt
Run Code Online (Sandbox Code Playgroud)

需要逐步了解此awk构造如何找到丢失的行。

Kus*_*nda 8

该脚本输出第二个文件中没有出现在第一个文件中的任何行。

awk 'NR==FNR { b[$0] = 1; next } !b[$0]' 1.txt 2.txt
Run Code Online (Sandbox Code Playgroud)

awk脚本开始与比较NRFNRNR是迄今为止读取的记录(行)总数,包括当前记录。 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详细信息,请参阅系统手册。


解决评论中的问题:

  1. Yes,FNR是从当前输入文件读取的记录数。
  2. NR并且FNR不“属于”任何一个文件,它们只是计数器。当FNR到达文件末尾时,计数器会重置。
  3. 双方NRFNR都在从文件中读取一行递增。该next命令强制跳转到脚本的开头,这也会导致读取下一行。NR并且FNR由于读取新行而通过此操作递增。
  4. 如果NR != FNR那么这意味着我们已经通过了第一个文件。FNR在到达前一个文件的末尾时被重置为零,但NR只是继续计数。
  5. $0是一个保存当前行的变量。它保存从文件中读取的完整行。$1$2等保存字段当前行为对变量的值分割的IFS(通常是任何空白)。如果当前行是hello world$0则将具有该值hello world$1具有该值hello$2具有该值world(因为该行在空间上被拆分)。该脚本仅使用$0,您可能会将其$0视为“当前输入行的内容”。
  6. b[$0] = 1是将值分配给数组中的特定位置/索引b。位置由当前行决定,$0赋值为1。这使得数组b就像一个“查找表”;如果b[i]对于任何特定索引为 1 i,那么这意味着它在第一个输入文件中被看到。
  7. !b[$0]如果存储在索引处$0b值为零(或未初始化),即如果b[$0]从未分配值 1,即从第二个文件中读取的行先前未在第一个文件中看到,则为真。由于{...}本次测试没有对应的动作(无块),所以$0执行打印的默认动作。这具有打印第二个文件中第一个文件中不存在的每一行的效果。