Как получить диапазон данных в наборе данных в миллион строк

#bash #text #awk #sed #grep

#bash #текст #awk #sed #grep

Вопрос:

У меня есть файл с миллионами строк, который имеет следующий шаблон

 01/02/1991,0931,7.13,7.13,7.13,7.13,8000

01/02/1991,0932,7.14,7.14,7.14,7.14,8000

01/02/1991,0935,7.16,7.16,7.16,7.16,3200

01/02/1991,0938,7.16,7.17,7.16,7.17,19200

01/02/1991,0941,7.19,7.19,7.19,7.19,200000 

01/02/1991,0956,7.19,7.19,7.19,7.19,8800
  

Формат даты mm/dd/YYYY . Как я могу получить только диапазон данных между 01/01/2002 и 31.08.2008 и удалить другие строки? Сами даты 01/01/2002 и 31.08.2008 могут отсутствовать в файле.

Я хотел бы сделать это в bash

Головной файл:

 01/02/1991,0931,7.13,7.13,7.13,7.13,83200^M$
01/02/1991,0932,7.14,7.14,7.14,7.14,8000^M$
01/02/1991,0935,7.16,7.16,7.16,7.16,3200^M$
01/02/1991,0938,7.16,7.17,7.16,7.17,19200^M$
01/02/1991,0941,7.19,7.19,7.19,7.19,200000^M$
01/02/1991,0956,7.19,7.19,7.19,7.19,8800^M$
01/02/1991,0957,7.20,7.20,7.20,7.20,13600^M$
01/02/1991,0958,7.22,7.22,7.22,7.22,5600^M$
01/02/1991,1003,7.22,7.23,7.22,7.23,8000^M$
01/02/1991,1006,7.23,7.23,7.23,7.23,10400^M$
  

Комментарии:

1. Записи уже отсортированы по дате?

2. Да, они уже отсортированы

3. Действительно ли строки расположены с двойным интервалом? Кстати, это не bash так, это sed . И ваша «попытка» смешна. Вы вообще читали руководство?

4. Я новичок в том, что касается sed и awk, и я разместил это здесь только потому, что мне не хотелось пробовать разные команды. Я понимаю, что это нелепый пример, но я поместил его туда, просто чтобы показать, что я сделал свою домашнюю работу. Да, в файле нет этого места в середине

5. Мне также нужны строки в середине, не только те, которые относятся к этим двум датам, я попробую их позже

Ответ №1:

Используя awk , вы можете сделать:

 awk -F, '$1=="01/02/2002"{p=1} $1=="01/08/2008"{p=2} $1!="01/08/2008" amp;amp; p==2{exit} p' dataset.txt
  

Комментарии:

1. Жаль, что дата не отформатирована в разумном формате ГГГГ-мм-дд, чтобы вы могли написать $1 > "2008-08-31" {exit}

2. Да, это было бы неплохо иметь.

3. @anubhava Большое вам спасибо за вашу помощь. Однако я должен сказать, что он ничего не делает. Он проходит через файл, но в конце процесса он имеет такое же количество строк.

4. @QuentinTanioartino Вам нужно перенаправить вывод в другой файл.

5. Как я и подозревал ранее, обе даты 01/01/2002 or 31/08/2008 не существуют в вашем входном файле. Попробуйте эту команду, чтобы заставить ее работать: awk -F, '$1=="01/02/2002"{p=1} p; $1=="01/08/2008"{exit}' AA.txt

Ответ №2:

Предложение sed :

 sed -n '#01/01/2002#,#31/08/2008#{p;d}; #31/08/2008#p' 
   dataset.txt > newFile.txt
  

Пока строки находятся между шаблонами, распечатайте, удалите из пространства шаблонов и завершите работу. 1-я строка конечного шаблона завершается из-за d того, что есть только одна печать. Следующие строки конечного шаблона печатаются с помощью второй команды.

Комментарии:

1. Но не будет ли это печатать только первую запись со второй датой? Что делать, если существует более одной записи с этой датой?

2. @ooga: все эти записи напечатаны для меня (вот почему я использовал {N,p} ). Вы пробовали это?

3. Ты меня поймал! Я его не запускал. Но теперь, когда я это сделал, кажется, печатается только одно дополнительное повторение записи со второй датой. Т.е., если есть 3 или более записей с конечной датой, печатаются только первые две. Однако я удаляю свой downvote, поскольку с моей стороны было небрежно делать это, не запуская его. (РЕДАКТИРОВАТЬ: по-видимому, вы не можете удалить понижающий голос через определенное время. Если вы отредактируете его, я думаю, что смогу. Может быть, вы сможете это исправить.)

4. Вот один из способов исправить это: sed -n '#01/01/2002#,#31/08/2008#{p;d}; #31/08/2008#p' file > outfile

5. @ooga: Вы правы, я протестировал свой код, но, похоже, этого тоже было недостаточно. Я редактирую свой пост с вашим исправлением. Спасибо!

Ответ №3:

Вот альтернатива: сравните даты как время. Это будет намного медленнее, чем решение anubhava. Требуется GNU awk:

 gawk -F, -v start_date=01/01/2002 -v end_date=08/31/2008 '
    function to_epoch(date) {
        #             ... year .....     ... month ......     ... day ........
        return mktime(substr(date,7) " " substr(date,1,2) " " substr(date,4,2) " 0 0 0")
    }
    BEGIN { start = to_epoch(start_date); end = to_epoch(end_date) }
    { t = to_epoch($1) }
    start <= t amp;amp; t <= end
    t > end {exit}
' file
  

Комментарии:

1. Дамы и господа, у нас есть победитель …… с некоторыми проблемами. Ваш скрипт — единственный, который работает должным образом …. Однако скрипт начинает получать данные с 01/01/2000, а не с 2002 года. и finisch 19/01/2007 не 2008

2. Ха, не в моем тестировании. Можете ли вы показать минимальную выборку входных данных, чтобы продемонстрировать эту проблему?

3. Хорошо, я отредактировал ответ. В вашем вопросе вы сказали нам, что формат даты был dd/mm/YYYY — это на самом деле mm/dd/YYYY . Так 31/08/2008 что никогда не появляется в файле!

Ответ №4:

 awk -F, '$1=="01/01/2002",$1=="31/08/2008" {print;next} $1=="31/08/2008" {print;next} {quit}' file
  

Добавлено дополнительное $1=="31/08/2008" для печати всех записей с этой датой (а не только первой). И добавил quit для повышения эффективности. Улучшения полностью благодаря anubhava.

Комментарии:

1. Просто, но не работает (будет напечатана только первая запись с датой "31/08/2008"

2. Иначе бы не прокомментировал.

3. @anubhava О, я понимаю, что ты имеешь в виду. Извините. 🙁

4. Сейчас это работает, но, поскольку OP говорит о миллионах строк, было бы лучше вызвать exit , как только запись пройдет "31/08/2008"

5. @anubhava Отличная точка зрения. Теперь я понимаю сложность вашего ответа.