Вопрос интересный, но не такой простой, как можно себе представить, особенно если у вас нет опции --total
.
Несколько вещей о comm
:
comm
работает с отсортированными файлами , если строка появляется n раз в file1
и m раз n раз в file2
, comm
выведет nm записей в столбце 2 и n записей в столбце 3.
$ comm <(echo -e "1\n2\n3") <(echo "2\n2\n3\n4")
1
2
2
3
4
comm
использует -характер в качестве разделителя по умолчанию, обработка его вывода становится проблематичной, если ваш ввод содержит этот символ.
$ comm <(echo -e "1\t2\n3") <(echo "2\n3\n4")
1 2 << this is the weird line
2
3
4
К счастью, у него есть возможность определить разделитель (--output-delimiter=STR
)
comm
добавляет разделитель, только если другие непустые поля следуют
$ comm --output-delimiter=SEP <(echo -e "1\n2\n3") <(echo "2\n3\n4")
1 << NO SEP (1 field)
SEPSEP2 << TWO SEP (3 fields)
SEPSEP3 << TWO SEP (3 fields)
SEP4 << ONE SEP (2 fields)
Как мы можем решить это сейчас:
Мы явно не должны использовать символ ASCII в качестве разделителя, это вызывает проблемы при обработке файлов ASCII, так что выможно использовать непечатаемый символ в качестве разделителя.Вы могли бы использовать, например,-символ с восьмеричным значением \001
(он не принимает -характер).Как правило, это решает проблемы, которые могут возникнуть из-за пункта (3)
$ comm --output-delimiter=$'\001' <(echo -e "1\t2\n3") <(echo "2\n3\n4")
, этот вывод теперь может быть передан в виде чрезвычайно простого awk
$ awk -F "\001" '{a[NF]++}END{print a[1],a[2],a[3] }'
, описанного выше работает из-заточка (4).
Так что вы можете просто сделать:
$ comm --output-delimiter=$'\001' file1 file2 \
| awk -F "\001" '{a[NF]++}END{print a[1],a[2],a[3] }'
Но у меня нет этой опции --output-delimiter
: Это требует чистого awk
решение.Мы отслеживаем 3 массива.a
для file1
b
для file2
и c
для комбинации.(c
отслеживает все записи).Мы обязательно учитываем пункт (2).
$ awk '(NR==FNR) { a[$0]++; c[$0]++ }
(NR!=FNR) { b[$0]++; c[$0]-- }
END { for(i in c) {
if (c[i] < 0) { countb+=-c[i]; countc+=a[i] }
else if (c[i] == 0) { countc+=a[i] }
else { counta+= c[i]; countc+=b[i] }
}
print counta, countb, countc
}' file1 file2
Мы могли бы по существу избавиться от массива b
, так как он может быть получен из a
и c
, но я хотелсделайте немного более понятным, как это работает;другая версия будет:
$ awk '(NR==FNR) { a[$0]++; c[$0]++; next } { c[$0]-- }
END { for(i in c) {
counta+=(c[i]>0 ? c[i] : 0)
countb-=(c[i]<0 ? c[i] : 0)
countc+=a[i] - (c[i]>0 ? c[i] : 0)
}
print counta, countb, countc
}' file1 file2