Извлечение уникальных значений между 2 наборами/файлами

Работа в linux/shell env, как я могу выполнить следующее:

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

1
2
3
4
5

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

6
7
1
2
3
4

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

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

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

Ответы

Ответ 1

$ awk 'FNR==NR {a[$0]++; next} !a[$0]' file1 file2
6
7

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

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

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

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

Ответ 2

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

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

Это выведет дубликаты, поэтому, если в file1 есть 1 3, но 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 имеется множество утилит, которые позволяют использовать все виды текстовых манипуляций.

Ответ 3

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

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, предположительно, использовать больше ОЗУ и меньше CPU. Реальные времена выполнения ниже для comm, вероятно, из-за того, что он использует больше потоков. Время CPU ниже для 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

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

awk 'FNR==NR{a[$0]=1;next}!($0 in a)' file1 file2

Ответ 4

с grep:

grep -F -x -v -f file_1 file_2 

Ответ 5

Как насчет:

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

Это будет печатать записи в файле_2, которые не находятся в файле_1. Для противоположного результата нужно просто заменить ' > ' на '<'. "cut" удаляет первые два символа, добавленные "diff", которые не являются частью исходного содержимого.

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

Ответ 6

здесь еще одно awk-решение

$ awk 'FNR==NR{a[$0]++;next}(!($0 in a))' file1 file2
6
7

Ответ 7

Если вы действительно настроены на выполнение этого из командной строки, этот сайт (поиск "без дубликатов найден" ) имеет 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), но он выполнит эту работу.