Как и предполагал @mjv, awk (или Perl, или Python) - лучший выбор:
awk -F';' ' {
if (assoc[$2]) { # This field 2 has been seen before
if (assoc[$2] != 1) { # The first occurrence has not been printed
print assoc[$2]; # Print first line with given $2
assoc[$2] = 1; # Reset array entry so we know we've printed it;
# a full line has 8 fields with semi-colons and
# cannot be confused with 1.
}
print $0; # Print this duplicate entry
}
else {
assoc[$2] = $0; # Record line in associative array, indexed by
# second field.
}
}' <<!
a;b;c;d;e;f;g;h
a;c;c;d;e;f;g;h
a;1;c;d;e;f;g;h
a;1;c;d;e;f;g;h
a;2;c;d;e;f;g;h
a;z;c;d;e;f;g;h
a;q;c;d;e;f;g;h
a;4;c;d;e;f;g;h
a;1;c;d;e;f;g;h
a;1;c;d;e;f;g;h
a;x;c;d;e;f;g;h
a;c;c;d;e;f;g;h
a;1;c;d;e;f;g;h
a;q;c;d;e;f;g;h
a;4;c;d;e;f;g;h
!
Это работает, но может немного переупорядочить данные - потому что печатает первое вхождение дублированной строки при появлении второго вхождения. Пример вывода:
a;1;c;d;e;f;g;h
a;1;c;d;e;f;g;h
a;1;c;d;e;f;g;h
a;1;c;d;e;f;g;h
a;c;c;d;e;f;g;h
a;c;c;d;e;f;g;h
a;1;c;d;e;f;g;h
a;q;c;d;e;f;g;h
a;q;c;d;e;f;g;h
a;4;c;d;e;f;g;h
a;4;c;d;e;f;g;h
Этот вариант сценария awk
переупорядочивает тестирование, что приводит к несколько более компактной записи. Он также явно игнорирует искаженные строки данных, которые не содержат 8 полей, разделенных точками с запятой. Он упакован как сценарий оболочки, но без какой-либо обработки опций, поэтому вы можете предоставить только список файлов для сканирования (он считывает стандартный ввод, если в списке нет файлов). Я удалил точки с запятой в сценарии; awk
не нуждается в них.
#!/bin/sh
awk -F';' '
NF == 8 {
if (!assoc[$2]) assoc[$2] = $0
else if (assoc[$2] != 1)
{
print assoc[$2]
assoc[$2] = 1
print $0
}
else print $0
}' "$@"
Кроме того, @mjv отметил, что могут быть проблемы с памятью при решении, подобном этому, если входные данные огромны, поскольку он хранит запись каждого отдельного значения поля 2 в ассоциативном массиве «assoc». Мы можем исключить, что если данные, введенные в awk
, отсортированы, то, что мы можем гарантировать, используя sort
, конечно. Вот вариант сценария, который работает с чудовищными входными данными (потому что sort
выливает данные на диск, если необходимо хранить промежуточные результаты):
sort -t';' -k 2,2 "$@" |
awk -F';' '
BEGIN { last = ";"; line = "" }
NF == 8 {
if ($2 != last)
{
last = $2
line = $0
}
else if (line != "")
{
print line
line = ""
print $0
}
else print $0;
}'
Это сохраняет только одну строку ввода. Разумеется, выходные данные из данных приведены в отсортированном порядке.