сортировать | uniq | xargs grep ... где строки содержат пробелы - PullRequest
4 голосов
/ 05 марта 2009

У меня есть файл с разделителями-запятыми "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?

Ответы [ 5 ]

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

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

Итак:

tr '\t' ',' < myfile.csv | sort -k5,5 | uniq -f 4 -D -w 16
2 голосов
/ 05 марта 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 в трубе была неправильной.

1 голос
/ 05 марта 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.)

0 голосов
/ 09 марта 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. Распечатайте номера строк для каждой даты.
0 голосов
/ 05 марта 2009

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

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