сортировать | уникальный | xargs grep, где строки содержат пробелы

У меня есть файл с разделителями-запятыми "myfile.csv", где 5-й столбец - это отметка даты/времени. (мм/дд/гггг чч:мм). Мне нужно перечислить все строки, содержащие повторяющиеся даты (их много)

Я использую оболочку bash через cygwin для WinXP.

$ cut -d, -f 5 myfile.csv | sort | uniq -d 

правильно возвращает список повторяющихся дат

01/01/2005 00:22
01/01/2005 00:37
[snip]    
02/29/2009 23:54

Но я не могу понять, как скормить это grep, чтобы получить все строки. Очевидно, я не могу использовать xargs напрямую, так как вывод содержит пробелы. Я думал, что смогу сделать uniq -z -d, но по какой-то причине объединение этих флагов приводит к тому, что uniq (очевидно) ничего не возвращает.

Итак, учитывая, что

 $ cut -d, -f 5 myfile.csv | sort | uniq -d -z | xargs -0 -I {} grep '{}' myfile.csv

не работает... что делать?

Я знаю, что мог бы сделать это в perl или другом языке сценариев... но мой упрямый характер настаивает на том, чтобы я мог сделать это в bash, используя стандартные инструменты командной строки, такие как sort, uniq, find, grep, cut и т. д.

Научите меня, о баш-гуру. Как я могу получить список нужных мне строк с помощью обычных инструментов cli?


person Sukotto    schedule 04.03.2009    source источник


Ответы (5)


  1. sort -k5,5 выполнит сортировку по полям и избежит вырезания;
  2. uniq -f 4 игнорирует первые 4 поля для uniq;
  3. Плюс -D в uniq даст вам все повторяющиеся строки (по сравнению с -d, которая даст вам только одну);
  4. но uniq будет ожидать разделителей табуляции вместо csv, поэтому tr '\t' ',' чтобы исправить это.

Проблема в том, что если у вас есть поля после № 5, которые отличаются. У вас все свидания одинаковой длины? Возможно, вы сможете добавить -w 16 (чтобы включить время) или -w 10 (только даты) в файл uniq.

So:

tr '\t' ',' < myfile.csv | sort -k5,5 | uniq -f 4 -D -w 16
person Andrew Barnett    schedule 04.03.2009
comment
Да +1. и tr '\t' ',' в конце, если важен формат CSV. - person kmkaplan; 05.03.2009

Опция -z для uniq требует, чтобы ввод был разделен NUL. Вы можете отфильтровать вывод cut через:

tr '\n' '\000'

Чтобы получить ноль разделенных строк. Тогда у sort, uniq и xargs есть опции для этого. Попробуйте что-то вроде:

cut -d, -f 5 myfile.csv | tr '\n' '\000' | sort -z | uniq -d -z | xargs -0 -I {} grep '{}' myfile.csv

Редактировать: положение tr в трубе было неправильным.

person kmkaplan    schedule 04.03.2009
comment
tr '\n' '\000' --- именно то, что я искал - person Felipe Alvarez; 07.05.2011

Вы можете указать xargs использовать каждую строку в качестве аргумента полностью, используя параметр -d. Пытаться:

cut -d, -f 5 myfile.csv | sort | uniq -d | xargs -d '\n' -I '{}' grep '{}' myfile.csv
person Andru Luvisi    schedule 04.03.2009

Это хороший кандидат на awk:

BEGIN { FS="," }
{ split($5,A," "); date[A[0]] = date[A[0]] " " NR }
END { for (i in date) print i ":" date[i] }
  1. Установите разделитель полей на «,» (CSV).
  2. Разделите пятое поле на пробел, закрепите результат A.
  3. Объедините номер строки со списком того, что мы уже сохранили на эту дату.
  4. Распечатайте номера строк для каждой даты.
person porges    schedule 09.03.2009

Попробуйте избежать пробелов с помощью sed:

echo 01/01/2005 00:37 | sed 's/ /\\ /g'
cut -d, -f 5 myfile.csv | sort | uniq -d | sed 's/ /\\ /g' | xargs -I '{}' grep '{}' myfile.csv

(Еще один способ - прочитать повторяющиеся строки даты в массив IFS=$'\n' и перебрать его в цикле for.)

person Community    schedule 05.03.2009
comment
Исправление: в выражении sed должно быть две обратные косые черты echo 01.01.2005 00:37 | sed 's/ /\\\\ /g' - person ; 05.03.2009