Сценарий выводит любую строку во втором файле, которая не встречается в первом файле.
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
, который мы заполнили ранее. Если для текущей записи в b
хранится 1, то мы знаем, что она ранее была найдена в первом файле. !
отрицает тест.
Если проверка верна, т.е. если текущая строка из второго файла ранее не встречалась в первом файле, то выполняется действие по умолчанию. Действие по умолчанию для теста без соответствующего блока {...}
— вывести текущую строку (, т. е. действует так, как если бы код был!b[$0] { print }
).
Поскольку этот awk
скрипт считывает в память все (уникальных )строк из первого файла,возможно, не стоит работать с очень большими файлами.
В таких случаях может быть лучше сделать что-то вроде
comm -13 <( sort -u file1 ) <( sort -u file2 )
(для этого требуется оболочка, которая знает о подстановках процессов )или просто
comm -13 file1 file2
если файлы уже отсортированы.
Это не генерирует точный такой же вывод, как скрипт awk
будет выводить любую строку из file2
, которая встречается несколько раз один раз при каждом ее появлении, тогда как команда comm
выше победила. 't, если на входе используется 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
. Это приводит к печати каждой строки из второго файла, которой нет в первом файле.