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

195

У меня есть файл .csv, как это:

stack2@example.com,2009-11-27 01:05:47.893000000,example.net,127.0.0.1
overflow@example.com,2009-11-27 00:58:29.793000000,example.net,255.255.255.0
overflow@example.com,2009-11-27 00:58:29.646465785,example.net,256.255.255.0
...

Я должен удалить дубликаты электронных писем (всю строку) из файла (то есть одну из строк, содержащихся overflow@example.comв приведенном выше примере). Как использовать uniqтолько поле 1 (через запятую)? По словам man, uniqне имеет опций для столбцов.

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

Eno
источник

Ответы:

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

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

overflow@domain2.com,2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0 
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 
Карл Смотриц
источник
3
это не работает, если столбец содержит саму запятую (с цитатой)
user775187
13
зачем вам, 1 в -k1,1? почему не просто -k1?
hello_there_andy
18
@hello_there_andy: это объясняется в руководстве ( man sort). Он обозначает начальную и конечную позицию.
Серрано
3
@CarlSmotricz: Я проверил его и он подтвердил , что sortговорит страница руководство «s:„ с , проверка строгого упорядочения, без , выход только первый из равной перспективы .“ Таким образом, это действительно «первое появление дубликата перед сортировкой». -u--unique-c-c
Геремия
2
это также меняет порядок строк, не так ли?
ркачач
104
awk -F"," '!_[$1]++' file
  • -F устанавливает разделитель полей.
  • $1 это первое поле.
  • _[val]смотрит valв хеш _(обычная переменная).
  • ++ увеличить и вернуть старое значение.
  • ! возвращает логическое нет.
  • в конце есть неявная печать.
ghostdog74
источник
4
Этот подход в два раза быстрее, чем сортировка
bitek
9
Это также имеет дополнительное преимущество, заключающееся в сохранении линий в исходном порядке!
AffluentOwl
8
Если вам нужен последний uniq вместо первого, тогда этот скрипт на awk поможет:awk -F',' '{ x[$1]=$0 } END { for (i in x) print x[i] }' file
Sukima
4
@eshwar просто добавь больше полей в словарь индекса! Например, !_[$1][$2]++можно использовать сортировку по первым двум полям. Однако мой awk-фу недостаточно силен, чтобы иметь возможность уникальности в разных областях. :(
Сохам Чоудхури
1
Brilliant! этот вариант лучше, чем ответ, потому что он сохраняет порядок строк
rkachach
16

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

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

sort -u -t : -k 1,1 -k 3,3 test.txt
  • -t : двоеточие является разделителем
  • -k 1,1 -k 3,3 на основе столбца 1 и столбца 3
Пракаш
источник
8

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

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

дает:

1 01:05:47.893000000 2009-11-27 tack2@domain.com
2 00:58:29.793000000 2009-11-27 overflow@domain2.com
1
Карстен С.
источник
5
Я хотел бы указать на возможное упрощение: вы можете сбросить cat! Вместо того, чтобы отправлять сообщения в tr, просто позвольте tr прочитать файл, используя <. Пропускание труб catявляется распространенным ненужным осложнением, используемым новичками. Для больших объемов данных есть эффект производительности.
Карл Смотриц
4
Хорошо знать. Спасибо! (Конечно, это имеет смысл, думая о «кошке» и «ленивости»;))
Карстен С.
Обращение полей можно упростить с помощью rev.
Хильке Валинга
5

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

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

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

Вот

tac перевернет файл построчно

Sumukh
источник
1

Вот очень изящный способ.

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

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

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

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

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 }'
NOYB
источник
-3

ну, проще, чем изолировать столбец с помощью 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 должен быть что-то вроде «[^ |] +» (т. е. что-либо, кроме разделителя). Но, надеюсь, это достаточно ясно.

Стив Б.
источник
3
Он не хочет очищать строки, он хочет сохранить одну копию строки с определенной строкой. Uniq - правильный вариант использования.
здесь
-3

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

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

$ cat test.csv
overflow@domain2.com,2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
overflow@domain2.com,2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack3@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack4@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1

$ sort test.csv
overflow@domain2.com,2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 
overflow@domain2.com,2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack3@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack4@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1

$ sort test.csv | uniq
overflow@domain2.com,2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 
overflow@domain2.com,2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack3@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack4@domain.com,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
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack4@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack3@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
overflow@domain2.com,2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 
Микаэль С
источник
Это не уникально по столбцу, как было указано в вопросе. Это просто уникально для всей линейки. Кроме того, вам не нужно делать сортировку, чтобы сделать уникальный. Два являются взаимоисключающими.
Джавид Джамае
1
Да ты прав. Последний пример делает то, что задал вопрос, хотя принятый ответ намного чище. Что касается sort, тогда uniq, sortдолжно быть сделано, прежде чем делать uniqиначе, это не работает (но вы можете пропустить вторую команду и просто использовать sort -u). From uniq(1): «Фильтровать соседние совпадающие строки из INPUT (или стандартного ввода), записывая в OUTPUT (или стандартный вывод)».
Микаэль С.
Ах, вы правы в сортировке перед Uniq. Я так и не понял, что uniq работает только на соседних линиях. Я думаю, я всегда просто использую сортировку -u.
Джавид Джамае