удалить дубликаты записей в файле, игнорируя уникальный идентификатор

С (в основном )баш:

Во входной файл (добавлена ​​строка, чтобы увидеть, что происходит с неоднородными -полями.

1   B,B,B,B C,C,C,C D,D,D,D
2   A,A,A,A B,B,B,B E,E,E,E
3   L,M,M   N,O,N   X,Y,Z

Функция для удаления дубликатов из слова, разделенного разделителем -. Я использую круглые скобки для тела функции, чтобы изолировать изменения в $IFSв подоболочке. Оставить $2без кавычек — это волшебство.

uniqify() (
    IFS=$1
    printf "%s\n" $2 | sort -u | paste -sd"$1"
)

Для обработки файла.

while read -ra words; do 
    for word in "${words[@]}"; do 
        uniqify, "$word"
    done | paste -s
done < input

Выход

1   B   C   D
2   A   B   E
3   L,M N,O X,Y,Z
-4
19.08.2019, 12:03
4 ответа

Для чистого ввода, разделенного запятыми -, вам может подойти awkскрипт, подобный следующему:

awk -F, '{X=""; for (i=2;i<29;i++) X=X " " $i;} \
     seen[X]!=1 {print;} \
     {seen[X]=1;}' < input

Первое awkправило создает «ключ» с помощью Cherry -, выбирая «слова» со 2 по 28 из ввода (, где согласно аргументу -F,все, что отделено запятой, является « слово" ).Следующее правило печатает строку, если «ключ» не был зарегистрирован, а третье правило затем регистрирует ключ для строки.

0
28.01.2020, 05:20

Ваш образец ввода перепутан -1-я строка (заголовки столбцов )даже не имеют запятых-разделителей полей -и в большинстве строк нет запятой между полями фамилии и оценки.

Чтобы обеспечить несколько -разумный ввод, я отредактировал его, чтобы он выглядел следующим образом:

$ cat input.txt 
ID, uid ,firstname,lastname,   grade   , country n28
1, ed23, jon    ,   doe  ,  director ,  usa
2, ed23,  jon    ,  doe  ,  director    , usa
3, er67,  jake    , Kogan,  director    , usa
4, er67,  jake    , Kogan,  director    , usa
5, dc10,  Charls    ,Morg,  manager     , usa
6, kc56,  patel    ,Kumar,  associate   , india

Простая реализация, просто исключающая дубликаты, будет примерно такой:

$ awk -F' *, *' -v OFS=, \
    'NR==1 {$1=$1;$0=$0; print; next};
     {id=$1; $1=""; $0=$0; if (!seen[$0]++) {print id $0}}' input.txt 
ID,uid,firstname,lastname,grade,country n28
1,ed23,jon,doe,director,usa
3,er67,jake,Kogan,director,usa
5,dc10,Charls,Morg,manager,usa
6,kc56,patel,Kumar,associate,india

Это устанавливает разделитель полей ввода(FS)равным нулю -или -больше пробелов, за которыми следует запятая, затем ноль -или -больше пробелов, а разделитель полей вывода(OFS)просто запятая. то есть он эффективно удаляет начальные и конечные пробелы из всех полей.

Для первой строки ввода(NR==1)используется awk-трюк для переформатирования строки ввода :, изменения любого из полей (, даже установки исходного значения ), а затем установки $0=$0. Линия будет переформатирована для использования нового OFS. затем он печатает его и переходит к следующей строке.

Для остальных входных данных он сохраняет $1 в переменной с именем id, устанавливает $1 в пустую строку, а затем снова использует трюк $0=$0(, эффективно удаляя $1 из строки )перед печатью идентификатора. и остаток строки.

В отличие от вашего примера вывода, здесь печатается первая из любых повторяющихся строк,не последний -очень легко определить, когда вы видите что-то в первый раз, но сложнее определить, когда вы собираетесь это увидеть в последний раз (вы не узнаете, пока не прочитаете все введенные данные ). Кроме того, здесь не учитывается количество просмотров дубликата.

Чтобы сделать обе эти вещи, требуется прочитать весь входной файл перед созданием любого вывода, а также второй массив(ids)для отслеживания последних -увиденных идентификационных номеров дубликатов -, использующих в два раза больше памяти., что может быть значительным при 700 тыс. входных строк.

$ awk -F' *, *' -v OFS=, \
   'NR==1 {$1=$1;$0=$0",count";print;next};
   {id=$1; $1=""; $0=$0; seen[$0]++; ids[$0]=id};
   END { for (id in ids) {print ids[id] id, seen[id]} }' input.txt  | \
 sort -n
ID,uid,firstname,lastname,grade,country n28,count
2,ed23,jon,doe,director,usa,2
4,er67,jake,Kogan,director,usa,2
5,dc10,Charls,Morg,manager,usa,1
6,kc56,patel,Kumar,associate,india,1

sort -nиспользуется здесь, потому что ассоциативные массивы в awk неупорядочены, поэтому появляются в полу-случайном порядке. В GNU awk есть функция asort(), которая может сортировать массивы по значению, которое можно было бы использовать в массиве idsздесь, но )она не переносима, и b )ее легко передать по конвейеру в sort -n.

1
28.01.2020, 05:20

Я предполагаю, что файл имеет формат «простой CSV», то есть в данных нет встроенных запятых или встроенных новых строк.

$ tac file | awk -F, '{ key = $0; sub("[^,]*,", "", key) } !seen[key]++' | tac
2,ed23,jon,doe,director,usa
4,er67,jake,Kogan,director,usa
5,dc10,Charls,Morg,manager,usa
6,kc56,patel,Kumar,associate,india

Код awkв середине приведенного выше конвейера создаст строку, используемую в качестве ключа в хэше, из всех полей каждой строки, кроме первого. Он напечатает первое вхождение строки с определенным ключом и проигнорирует все дубликаты.

Поскольку вы, похоже, хотите получить последний дубликат, я изменю порядок строк во входных данных сtac(из GNU coreutils ), прежде чем передать их в программу awk. Затем я обращаю вывод программы awk.

Недостаток этого подхода состоит в том, что вычисляемые ключи будут использовать столько памяти, сколько суммарный размер всех уникальных строк за вычетом первого поля.

Следующий подход является -более эффективным с точки зрения памяти, но он предполагает, что ввод сортируется таким образом, что повторяющиеся строки всегда встречаются вместе:

$ tac file | awk -F, '{ key = $0; sub("[^,]*,", "", key) } key != prev; { prev = key }' | tac
2,ed23,jon,doe,director,usa
4,er67,jake,Kogan,director,usa
5,dc10,Charls,Morg,manager,usa
6,kc56,patel,Kumar,associate,india
0
28.01.2020, 05:20

Детализация подходаuniq-из комментариев выше:

$ tr ',' '\t' < temp/testfile | uniq -f 1 | tr '\t' ','
1,ed23,jon,doe,director,usa
3,er67,jake,Kogan,director,usa
5,dc10,Charls,Morg,manager,usa
6,kc56,patel,Kumar,associate,india

Использование \tв качестве разделителя, чтобы не забиваться пробелами в ваших данных.

uniqсохранит первую из найденных уникальных строк. Если вам абсолютно необходимо сохранить «последние» записи, вам нужно работать с конца до начала вашего файла. Вы можете сделать это, используяtac:

$ tac temp/testfile|tr ',' '\t' | uniq -f 1 | tr '\t' ','|tac
2,ed23,jon,doe,director,usa
4,er67,jake,Kogan,director,usa
5,dc10,Charls,Morg,manager,usa
6,kc56,patel,Kumar,associate,india

0
28.01.2020, 05:20

Теги

Похожие вопросы