发现下面的awk
命令可以找到与1.txt
相比缺少的行2.txt
。
awk 'NR==FNR{b[$0]=1;next}!b[$0]' 1.txt 2.txt
需要逐步了解该awk
构造如何找到缺失的行。
答案1
该脚本输出第二个文件中第一个文件中未出现的任何行。
awk 'NR==FNR { b[$0] = 1; next } !b[$0]' 1.txt 2.txt
该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 )
(这需要一个知道进程替换的 shell),或者只是
comm -13 file1 file2
如果文件已经排序。
这不会生成精确的与脚本相同的输出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
while ,您可能会将其视为$0
“当前输入行的内容”。b[$0] = 1
是将值分配给数组中的特定位置/索引b
。位置由当前行 确定,$0
分配的值为 1。这使得数组b
就像一个“查找表”;如果b[i]
任何特定索引为 1i
,则这意味着它在第一个输入文件中出现。!b[$0]
$0
如果存储在索引处的值b
为零(或者未初始化),即如果b[$0]
从未分配值1,即刚刚从第二个文件读取的行之前在第一个文件中没有看到,则为真。由于没有与此测试对应的动作(无块),因此执行{...}
打印的默认动作。$0
这具有打印第二个文件中第一个文件中不存在的每一行的效果。