Извлечение уникальных значений между 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), но он выполнит эту работу.