извлечение уникальных значений между 2 наборами / файлами - PullRequest
21 голосов
/ 17 января 2011

Работая в среде Linux / Shell, как я могу сделать следующее:

текстовый файл 1 содержит:

1
2
3
4
5

текстовый файл 2 содержит:

6
7
1
2
3
4

Мне нужно извлечь записи в файле 2, которых нет в файле 1. Так что «6» и «7» в этом примере.

Как мне это сделать из командной строки?

большое спасибо!

Ответы [ 7 ]

46 голосов
/ 17 января 2011
$ awk 'FNR==NR {a[$0]++; next} !a[$0]' file1 file2
6
7

Объяснение того, как работает код:

  • Если мы работаем с файлом file1, отследите каждую строку текста, которую мы видим.
  • Если мы работаем надfile2, и не видели текст строки, затем напечатайте его.

Объяснение деталей:

  • FNR - номер записи текущего файла
  • NR является текущим общим номером записи из всех входных файлов
  • FNR==NR верно только тогда, когда мы читаем file1
  • $0 является текущей строкой текста
  • a[$0] - это хеш с ключом, установленным на текущую строку текста
  • a[$0]++ дорожек, с которыми мы видели текущую строку текста
  • !a[$0] только trueкогда мы не видели текст строки
  • Напечатайте строку текста, если вышеприведенный шаблон возвращает true, это поведение по умолчанию в awk, когда не задано явное действие
14 голосов
/ 17 января 2011

Использование некоторых менее известных утилит:

sort file1 > file1.sorted
sort file2 > file2.sorted
comm -1 -3 file1.sorted file2.sorted

Это выдаст дубликаты, поэтому, если есть 1 3 в file1, но 2 в file2, это все равно будет выводить 1 3. Если это не то, что вам нужно, передайте вывод от sort до uniq перед записью в файл:

sort file1 | uniq > file1.sorted
sort file2 | uniq > file2.sorted
comm -1 -3 file1.sorted file2.sorted

В пакете GNU coreutils есть множество утилит, которые позволяют выполнять любые виды текстовых манипуляций.

8 голосов
/ 29 сентября 2013

Мне было интересно, какое из следующих решений было «самым быстрым» для «больших» файлов:

awk 'FNR==NR{a[$0]++}FNR!=NR && !a[$0]{print}' file1 file2 # awk1 by SiegeX
awk 'FNR==NR{a[$0]++;next}!($0 in a)' file1 file2          # awk2 by ghostdog74
comm -13 <(sort file1) <(sort file2)
join -v 2 <(sort file1) <(sort file2)
grep -v -F -x -f file1 file2

Коротко, результаты моих тестов:

  • Не использоватьgrep -Fxf, это намного медленнее (в моих тестах 2-4 раза).
  • comm немного быстрее, чем join.
  • Если file1 и file2 уже отсортированы, comm и join намного быстрее, чем awk1 + awk2.(Конечно, они не предполагают сортировку файлов.)
  • awk1 + awk2, предположительно, используют больше оперативной памяти и меньше ЦП.Реальное время выполнения меньше для comm, вероятно, из-за того, что он использует больше потоков.Время процессора меньше для awk1 + awk2.

Для краткости опущу полные детали.Тем не менее, я предполагаю, что любой желающий может связаться со мной или просто повторить тесты.Грубо говоря, установка была

# Debian Squeeze, Bash 4.1.5, LC_ALL=C, slow 4 core CPU
$ wc file1 file2
  321599   321599  8098710 file1
  321603   321603  8098794 file2

Типичные результаты самых быстрых запусков

awk2: real 0m1.145s  user 0m1.088s  sys 0m0.056s  user+sys 1.144
awk1: real 0m1.369s  user 0m1.324s  sys 0m0.044s  user+sys 1.368
comm: real 0m0.980s  user 0m1.608s  sys 0m0.184s  user+sys 1.792
join: real 0m1.080s  user 0m1.756s  sys 0m0.140s  user+sys 1.896
grep: real 0m4.005s  user 0m3.844s  sys 0m0.160s  user+sys 4.004

Кстати, для любителей: кажется, что a[$0]=1 быстрее, чем a[$0]++, и (!($0 in a)) быстрее (!a[$0]).Итак, для решения awk я предлагаю:

awk 'FNR==NR{a[$0]=1;next}!($0 in a)' file1 file2
5 голосов
/ 05 мая 2014

Как насчет:

diff file_1 file_2 | grep '^>' | cut -c 3-

Это напечатало бы записи в file_2, которых нет в file_1.Для противоположного результата нужно просто заменить «>» на «<».«cut» удаляет первые два символа, добавленные «diff», которые не являются частью исходного содержимого. </p>

Файлы даже не нужно сортировать.

5 голосов
/ 17 января 2011

с grep:

grep -F -x -v -f file_1 file_2 
2 голосов
/ 18 января 2011

вот еще одно решение awk

$ awk 'FNR==NR{a[$0]++;next}(!($0 in a))' file1 file2
6
7
0 голосов
/ 17 января 2011

Если вы действительно настроены на выполнение этого из командной строки, этот сайт (поиск "дубликаты не найдены") имеет пример awk, который ищет дубликаты.Это может быть хорошей отправной точкой, чтобы посмотреть на это.

Тем не менее, я бы посоветовал вам использовать Perl или Python для этого.По сути, поток программы будет выглядеть следующим образом:

findUniqueValues(file1, file2){
    contents1 = array of values from file1
    contents2 = array of values from file2
    foreach(value2 in contents2){
        found=false
        foreach(value1 in contents1){
            if (value2 == value1) found=true
        }
        if(!found) print value2
    }
}

Это не самый элегантный способ сделать это, поскольку он имеет временную сложность O (n ^ 2), но он сделает свою работу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...