#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 Отличная точка зрения. Теперь я понимаю сложность вашего ответа.