1. Входной файл имеет 3 поля. Каждое поле разделено символом | (ТРУБКА).
  2. Первое поле является ключевым полем и отсортировано. Каждый ключ в первом поле может встречаться один или два раза.
  3. Если один и тот же ключ существует дважды в первом поле, удалите строку первого вхождения и не удаляйте строку второго вхождения.
  4. Если ключ встречается только один раз, не удаляйте строку.
  5. Входные данные в третьем поле будут уникальными для всего файла.

Пробовал команду ниже, которая сохраняет первую повторяющуюся строку и удаляет остальные повторяющиеся строки. Есть ли в команде awk возможность удалить первую совпадающую повторяющуюся строку и сохранить вторую совпадающую строку. Команды, отличные от awk, тоже допустимы. Размер входного файла может составлять 50 ГБ. Сейчас тестирую на файле размером 12 ГБ.

awk -F'|' '!a[$1]++'

Содержимое входного файла:

1|xxx|{name: "xyz"}
2|xxx|{name: "abcfgs"}
3|xxx|{name: "egg"}
4|xxx|{name: "eggrgg"}
5|xxx|{name: "xsdsyzsgngn"}
5|xxx|{name: "gbgnfxyz"}
6|xxx|{name: "xyz"}
7|xxx|{name: "xynfnfnnnz"}
7|xxx|{name: "bvbv"}
8|xxx|{name: "xyz"}
9|xxx|{name: "xyz"}
....

Ожидаемый результат после обработки входного файла:

1|xxx|{name: "xyz"}
2|xxx|{name: "abcfgs"}
3|xxx|{name: "egg"}
4|xxx|{name: "eggrgg"}
5|xxx|{name: "gbgnfxyz"}
6|xxx|{name: "xyz"}
7|xxx|{name: "bvbv"}
8|xxx|{name: "xyz"}
9|xxx|{name: "xyz"}
....

ИЗМЕНИТЬ

Пробовали нижеприведенные решения, предоставленные @ RavinderSingh13 и @RomanPerekhrest соответственно.

Для входного файла размером 12 ГБ приведенное ниже решение заняло 1 минуту 20 секунд при первом запуске и 1 минуту 46 секунд при втором запуске:

awk '
BEGIN{
  FS="|"
}
!a[$1]++{
  b[++count]=$1
}
{
  c[$1]=$0
}
END{
  for(i=1;i<=count;i++){
    print c[b[i]]
  }
}
' Inputfile  > testawk.txt

Для входного файла 12 ГБ приведенное ниже решение заняло 2 минуты 31 секунду при первом запуске, 4 минуты 43 секунды при втором запуске и 2 минуты при третьем запуске:

awk -F'|' 'prev && $1 != prev{ print row }{ prev=$1; row=$0 }END{ print row }' Inputfile > testawk2.txt

Оба решения работают должным образом. Я буду использовать любой из вышеперечисленных после еще нескольких тестов производительности.

2
inzero 3 Дек 2019 в 14:46
Я не видел дублированного удаления из ожидаемого результата. Пожалуйста, дополните.
 – 
James Brown
3 Дек 2019 в 15:00
1
Это сделал. ключ $1
 – 
Kent
3 Дек 2019 в 15:01
А как насчет 3-го, 4-го, ... они должны быть удалены, как и 1-й? Как насчет случая НЕТ дубликата, вам все равно нужно увидеть первый экземпляр?
 – 
dash-o
3 Дек 2019 в 15:09
В вашем примере это префикс 5 | xxx | просто имеется ввиду строка 5 входного файла xxx? или это часть ввода?
 – 
dash-o
3 Дек 2019 в 15:10
Почему ожидаемый результат включает «xyz» из строки 1 - следует ли его удалить из-за «xyz» в строке 6?
 – 
dash-o
3 Дек 2019 в 15:11

4 ответа

Лучший ответ

1-е решение: . Если вас совсем не беспокоит порядок строк в выводе, просто сделайте это.

awk 'BEGIN{FS="|"} {a[$1]=$0} END{for(i in a){print a[i]}}' Input_file


2-е решение: добавление еще одного решения с awk меньшим количеством массивов и sort на случай, если вы беспокоитесь о порядке.

awk 'BEGIN{FS="|"} {a[$1]=$0} END{for(i in a){print a[i]}}' Input_file | sort -t'|' -k1


Третье решение . Не могли бы вы попробовать следующее. Если вас беспокоит порядок вывода, он должен быть таким же, как показано на Input_file.

awk '
BEGIN{
  FS="|"
}
!a[$1]++{
  b[++count]=$1
}
{
  c[$1]=$0
}
END{
  for(i=1;i<=count;i++){
    print c[b[i]]
  }
}
'  Input_file

Результат будет следующим.

1|xxx|{name: "xyz"}
2|xxx|{name: "abcfgs"}
3|xxx|{name: "egg"}
4|xxx|{name: "eggrgg"}
5|xxx|{name: "gbgnfxyz"}
6|xxx|{name: "xyz"}
7|xxx|{name: "bvbv"}
8|xxx|{name: "xyz"}
9|xxx|{name: "xyz"}
1
RavinderSingh13 3 Дек 2019 в 15:33
Имейте в виду, что OP сказал, что файл 50G. c[] хранит весь файл, если я правильно понял коды.
 – 
Kent
3 Дек 2019 в 15:25
@Kent, да, правда, позвольте мне поставить 2 раза концепцию чтения Input_file, но не уверен, будет ли это быстрее, чем это или нет.
 – 
RavinderSingh13
3 Дек 2019 в 15:27
@Kent, Привет, сэр, как насчет моего второго решения? ИМХО, этот должен быть быстрее, чем мой предыдущий, я считаю, что вы говорите, сэр.
 – 
RavinderSingh13
3 Дек 2019 в 15:32
1
Это не о том, что быстрее или медленнее. Я не знаю, что произойдет, если в системе OP будет <50 ГБ свободной памяти.
 – 
Kent
3 Дек 2019 в 15:32

Этот однострочный файл удалит только первый дубликат (второе вхождение) из вашего файла.

awk 'a[$1]++ !=1' file

Посмотрим на пример:

kent$  cat f
1
2
3
2 <- should be removed
4
3 <- should be removed
5
6
7
8
9
2 <- should be kept
3 <- should be kept
10

kent$  awk 'a[$1]++ !=1' f
1
2
3
4
5
6
7
8
9
2
3
10
0
Kent 3 Дек 2019 в 14:59
Для меня первое вхождение должно быть удалено, а второе вхождение должно присутствовать.
 – 
inzero
3 Дек 2019 в 15:03
Значит, вы хотите иметь только дублированные данные? Например. 5 в моем примере нет дубликатов, его стоит оставить или нет? пожалуйста, сделайте лучший пример ввода / вывода. Я совершенно запуталась.
 – 
Kent
3 Дек 2019 в 15:13
Если у вас есть 10 строк с одним и тем же ключом, вы хотите, чтобы на выходе были 2-10 строки?
 – 
Kent
3 Дек 2019 в 15:14
Будет только 1 или 2 строки с одинаковым ключом.
 – 
inzero
3 Дек 2019 в 15:15

Обратный файл и стабильная уникальная сортировка:

cat <<EOF |
1|xxx|{name: "xyz"}
2|xxx|{name: "abcfgs"}
3|xxx|{name: "egg"}
4|xxx|{name: "eggrgg"}
5|xxx|{name: "xsdsyzsgngn"}
5|xxx|{name: "gbgnfxyz"}
6|xxx|{name: "xyz"}
7|xxx|{name: "xynfnfnnnz"}
7|xxx|{name: "bvbv"}
8|xxx|{name: "xyz"}
9|xxx|{name: "xyz"}
EOF
tac | sort -s -t'|' -k1,1 -u

Выведет:

1|xxx|{name: "xyz"}
2|xxx|{name: "abcfgs"}
3|xxx|{name: "egg"}
4|xxx|{name: "eggrgg"}
5|xxx|{name: "gbgnfxyz"}
6|xxx|{name: "xyz"}
7|xxx|{name: "bvbv"}
8|xxx|{name: "xyz"}
9|xxx|{name: "xyz"}

tac - это утилита GNU. Поскольку ваш файл большой, передайте имя файла в tac, чтобы он мог прочитать файл с обратной стороны, и используйте опцию -T, --temporary-directory=DIR с sort, чтобы разрешить сортировку таких больших файлов (или нет, если хватит барана).

0
KamilCuk 3 Дек 2019 в 15:39

Эффективно с выражением awk :

awk -F'|' 'prev && $1 != prev{ print row }{ prev=$1; row=$0 }END{ print row }' file

«Магия» основана на захвате каждой текущей записи (эффективная перезапись ее без постоянного накопления) и выполнении анализа следующей строки.

Пример вывода:

1|xxx|{name: "xyz"}
2|xxx|{name: "abcfgs"}
3|xxx|{name: "egg"}
4|xxx|{name: "eggrgg"}
5|xxx|{name: "gbgnfxyz"}
6|xxx|{name: "xyz"}
7|xxx|{name: "bvbv"}
8|xxx|{name: "xyz"}
9|xxx|{name: "xyz"}
2
RomanPerekhrest 3 Дек 2019 в 15:40