Есть ли способ «uniq» по столбцу?

У меня есть файл .csv следующим образом:

[email protected],2009-11-27 01:05:47.893000000,example.net,127.0.0.1
[email protected],2009-11-27 00:58:29.793000000,example.net,255.255.255.0
[email protected],2009-11-27 00:58:29.646465785,example.net,256.255.255.0
...

Мне нужно удалить повторяющиеся электронные письма (всю строку) из файла (т.е. одну из строк, содержащих [email protected] в приведенном выше примере). Как использовать uniq только в поле 1 (через запятую)? Согласно man, uniq не имеет опций для столбцов.

Я пробовал кое-что с sort | uniq, но это не работает.


person Eno    schedule 16.12.2009    source источник


Ответы (8)


sort -u -t, -k1,1 file
  • -u для уникальных
  • -t, поэтому запятая является разделителем
  • -k1,1 для ключевого поля 1

Результат испытаний:

[email protected],2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0 
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 
person Carl Smotricz    schedule 16.12.2009
comment
зачем вам ,1 в -k1,1? почему не просто -k1? - person hello_there_andy; 24.11.2014
comment
@hello_there_andy: это объясняется в руководстве (man sort). Это означает стартовое и стоп-положение. - person Serrano; 27.01.2015
comment
Как он решает, какую строку с повторяющимся полем выводить? Является ли это первым появлением дубликата перед сортировкой? - person Geremia; 13.04.2016
comment
@CarlSmotricz: я протестировал его, и он подтвердил то, что написано на странице руководства sort: -u, --unique с -c, проверьте строгий порядок; без -c выводить только первый из равного запуска. Таким образом, это действительно первое появление дубликата перед сортировкой. - person Geremia; 15.04.2016
comment
это также меняет порядок строк, не так ли? - person rkachach; 20.03.2019
comment
Он отвечает на конкретный вопрос, но заголовок этого не отражает, т. е. существуют другие параметры 'uniq', к которым 'sort -u' не применяется, - например, просто сообщает, какие строки дублируются (и не производит вывод для уникальные строки). Интересно, почему 'uniq' имеет параметр '--skip-fields=N', но не имеет возможности выбрать поле для сравнения... это кажется очевидным. - person Max Waterman; 21.11.2019

awk -F"," '!_[$1]++' file
  • -F устанавливает разделитель полей.
  • $1 — первое поле.
  • _[val] ищет val в хеше _ (обычная переменная).
  • ++ увеличить и вернуть старое значение.
  • ! возвращает логическое "нет".
  • в конце есть неявный отпечаток.
person ghostdog74    schedule 16.12.2009
comment
Этот подход в два раза быстрее, чем sort - person Alex Bitek; 18.02.2015
comment
Это также имеет дополнительное преимущество, заключающееся в сохранении исходного порядка строк! - person AffluentOwl; 10.03.2015
comment
Если вам нужен последний uniq вместо первого, вам поможет этот awk-скрипт: awk -F',' '{ x[$1]=$0 } END { for (i in x) print x[i] }' file - person Sukima; 01.10.2015
comment
@eshwar просто добавьте больше полей в индекс словаря! Например, !_[$1][$2]++ можно использовать для сортировки по первым двум полям. Однако мой awk-fu недостаточно силен, чтобы быть уникальным в ряде полей. :( - person Soham Chowdhury; 29.01.2017
comment
Примечание. Вы хотите использовать это решение, если читаете со стандартного ввода: <cmnd> | awk -F"," '!_[$1]++' - - person marek094; 01.07.2018
comment
Великолепно! этот вариант лучше, чем ответ, потому что он сохраняет порядок строк - person rkachach; 20.03.2019
comment
У этого решения есть как плюсы, так и минусы: оно сохраняет первый столбец в памяти для каждой строки: может быть более жадным, чем sort -u для больших файлов. - person Corentin Limier; 02.07.2019

Чтобы рассмотреть несколько столбцов.

Отсортируйте и дайте уникальный список на основе столбца 1 и столбца 3:

sort -u -t : -k 1,1 -k 3,3 test.txt
  • -t : двоеточие является разделителем
  • -k 1,1 -k 3,3 на основе столбца 1 и столбца 3
person Prakash    schedule 21.01.2014

или если вы хотите использовать uniq:

<mycvs.cvs tr -s ',' ' ' | awk '{print $3" "$2" "$1}' | uniq -c -f2

дает:

1 01:05:47.893000000 2009-11-27 [email protected]
2 00:58:29.793000000 2009-11-27 [email protected]
1
person Carsten C.    schedule 16.12.2009
comment
Я хотел бы указать на возможное упрощение: вы можете сбросить cat! Вместо передачи в tr просто позвольте tr прочитать файл, используя <. Прокладка трубопровода через cat — обычное ненужное усложнение, используемое новичками. Для больших объемов данных необходимо иметь влияние на производительность. - person Carl Smotricz; 16.12.2009
comment
Инверсию полей можно упростить с помощью rev. - person Hielke Walinga; 09.07.2019

Если вы хотите сохранить последний из дубликатов, которые вы можете использовать

 tac a.csv | sort -u -t, -r -k1,1 |tac

Что было моим требованием

здесь

tac будет переворачивать файл построчно

person Sumukh    schedule 14.12.2015

Вот очень прикольный способ.

Сначала отформатируйте содержимое таким образом, чтобы столбец, который будет сравниваться на предмет уникальности, имел фиксированную ширину. Один из способов сделать это — использовать awk printf со спецификатором ширины поля/столбца ("%15s").

Теперь параметры -f и -w uniq можно использовать для пропуска предшествующих полей/столбцов и для указания ширины сравнения (ширины столбцов).

Вот три примера.

В первом примере...

1) Временно сделайте для интересующего столбца фиксированную ширину, большую или равную максимальной ширине поля.

2) Используйте опцию -f uniq, чтобы пропустить предыдущие столбцы, и используйте опцию -w uniq, чтобы ограничить ширину до tmp_fixed_width.

3) Удалите конечные пробелы из столбца, чтобы «восстановить» его ширину (при условии, что перед этим не было конечных пробелов).

printf "%s" "$str" \
| awk '{ tmp_fixed_width=15; uniq_col=8; w=tmp_fixed_width-length($uniq_col); for (i=0;i<w;i++) { $uniq_col=$uniq_col" "}; printf "%s\n", $0 }' \
| uniq -f 7 -w 15 \
| awk '{ uniq_col=8; gsub(/ */, "", $uniq_col); printf "%s\n", $0 }'

Во втором примере...

Создайте новый столбец uniq 1. Затем удалите его после применения фильтра uniq.

printf "%s" "$str" \
| awk '{ uniq_col_1=4; printf "%15s %s\n", uniq_col_1, $0 }' \
| uniq -f 0 -w 15 \
| awk '{ $1=""; gsub(/^ */, "", $0); printf "%s\n", $0 }'

Третий пример аналогичен второму, но для нескольких столбцов.

printf "%s" "$str" \
| awk '{ uniq_col_1=4; uniq_col_2=8; printf "%5s %15s %s\n", uniq_col_1, uniq_col_2, $0 }' \
| uniq -f 0 -w 5 \
| uniq -f 1 -w 15 \
| awk '{ $1=$2=""; gsub(/^ */, "", $0); printf "%s\n", $0 }'
person NOYB    schedule 25.04.2017

ну проще, чем изолировать столбец с awk, если нужно удалить все с определенным значением для данного файла, почему бы просто не сделать grep -v:

например удалить все со значением «col2» во второй строке: col1, col2, col3, col4

grep -v ',col2,' file > file_minus_offending_lines

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

awk, чтобы изолировать столбец-нарушитель: например.

awk -F, '{print $2 "|" $line}'

-F устанавливает поле с разделителем в ",", $2 означает столбец 2, за которым следует какой-либо пользовательский разделитель, а затем вся строка. Затем вы можете отфильтровать, удалив строки, которые начинаются с оскорбительного значения:

 awk -F, '{print $2 "|" $line}' | grep -v ^BAD_VALUE

а затем удалить материал перед разделителем:

awk -F, '{print $2 "|" $line}' | grep -v ^BAD_VALUE | sed 's/.*|//g'

(обратите внимание: команда sed небрежна, потому что не включает экранирующие значения. Кроме того, шаблон sed действительно должен быть чем-то вроде «[^|]+» (т. е. что угодно, кроме разделителя). Но, надеюсь, это достаточно ясно.

person Steve B.    schedule 16.12.2009
comment
Он не хочет очищать строки, он хочет сохранить одну копию строки с определенной строкой. Uniq — правильный вариант использования. - person ingyhere; 13.11.2015

Сначала отсортировав файл по sort, вы сможете затем применить uniq.

Кажется, файл отлично сортируется:

$ cat test.csv
[email protected],2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
[email protected],2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1

$ sort test.csv
[email protected],2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 
[email protected],2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1

$ sort test.csv | uniq
[email protected],2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 
[email protected],2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1

Вы также можете использовать магию AWK:

$ awk -F, '{ lines[$1] = $0 } END { for (l in lines) print lines[l] }' test.csv
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
[email protected],2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 
person Mikael S    schedule 16.12.2009
comment
Это не уникально по столбцу, как указано в вопросе. Это просто уникально для всей линейки. Кроме того, вам не нужно делать сортировку, чтобы сделать uniq. Эти два понятия являются взаимоисключающими. - person Javid Jamae; 24.09.2014
comment
Да, ты прав. Последний пример делает то, о чем задавался вопрос, хотя принятый ответ намного чище. Что касается sort, то uniq, sort нужно выполнить перед выполнением uniq иначе не получится (но можно пропустить вторую команду и просто использовать sort -u). Из uniq(1): фильтрация смежных совпадающих строк из INPUT (или стандартного ввода), запись в OUTPUT (или стандартный вывод). - person Mikael S; 25.09.2014