Чи є спосіб "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але не виходить.

Відповіді:


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, -к1,1? чому б не просто -k1?
hello_there_andy

18
@hello_there_andy: Це пояснено в посібнику ( man sort). Він означає позицію старту та зупинки.
Серрано

3
@CarlSmotricz: Я перевірив це, і це підтвердило те sort, що написано на сторінці на сторінці: " -u, --unique з -c, перевіряйте на чітке впорядкування; без -c, виведіть лише перший з рівного пробігу ". Отже, це справді "перше виникнення дубліката перед сортуванням".
Геремія

2
це також змінює порядок ліній, чи не так?
ркачач

104
awk -F"," '!_[$1]++' file
  • -F встановлює роздільник поля.
  • $1 є першим полем.
  • _[val]шукає valв хеші _(звичайна змінна).
  • ++ приріст і повернути старе значення.
  • ! повертає логічно не.
  • в кінці є неявна друк.

4
Цей підхід удвічі швидший за сортування
бітек

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
Блискуче! цей варіант кращий за відповідь, тому що він зберігає порядок рядків
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.
Hielke Walinga

5

Якщо ви хочете зберегти останній з дублікатів, який ви можете використати

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

Яка була моя вимога

тут

tac поверне файл рядок за рядком


1

Ось дуже витончений спосіб.

Спочатку відформатуйте вміст таким чином, щоб стовпець, який слід порівнювати за унікальністю, був фіксованою шириною. Один із способів зробити це - використовувати 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 }'

-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 - це правильний випадок використання.
ingyhere

-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 

Це не унікально за стовпчиком, про що про це йдеться у запитанні. Це просто унікально для всієї лінії. Крім того, вам не потрібно робити сортування, щоб зробити uniq. Двоє взаємовиключні.
Javid Jamae

1
Так, ти маєш рацію. Останній приклад робить те, про що було задано питання, хоча прийнята відповідь набагато чіткіша. Щодо sortтого uniq, це sortпотрібно зробити, перш ніж робити uniqінакше, це не працює (але можна пропустити другу команду і просто використовувати sort -u). З uniq(1): "Фільтр сусідніх відповідних рядків з INPUT (або стандартного вводу), запис до OUTPUT (або стандартного виводу)."
Mikael S

Ах, ти маєш рацію щодо сортування до uniq. Я ніколи не розумів, що uniq працює лише на суміжних лініях. Я думаю, я завжди просто використовую сорт -у.
Javid Jamae
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.