Форум русскоязычного сообщества Ubuntu


Следите за новостями русскоязычного сообщества Ubuntu в Twitter-ленте @ubuntu_ru_loco

Автор Тема: Как из csv в bash выбрать строки между определённых дат?  (Прочитано 453 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн morhad

  • Автор темы
  • Любитель
  • *
  • Сообщений: 50
    • Просмотр профиля
Вот если бы мне нужно было в csv файле, например, input.csv с датами вида 2016-12-04 17:46:04 отсортировать строки по возрастанию дат, то я бы просто применил команду sort. А как мне выбрать строки в промежутке от заданных мной дат?
Допустим в файле input.csv
Код: XML
  1. 2015-10-06 15:09:01;данные1
  2. 2016-12-03 10:00:04;данные2
  3. 2016-09-23 11-00-09;данные3
  4. 2016-12-01 09:22:00;данные4
  5. 2016-11-29 18:44:13;данные5
...
а мне нужно выбрать строки с датами в промежутке 2016-11-28 00:00:00 до 2016-12:05 00:00:00
Ну, наверное, я сначала бы написал команду sort input.csv и получил бы отсортированный выход вида:
Код: XML
  1. 2015-10-06 15:09:01;данные1
  2. 2016-09-23 11-00-09;данные3
  3. 2016-11-29 18:44:13;данные5
  4. 2016-12-01 09:22:00;данные4
  5. 2016-12-03 10:00:04;данные2

А дальше как выбрать строк из промежутка 2016-11-28 00:00:00 до 2016-12:05 00:00:00 ?


Пользователь добавил сообщение 04 Декабрь 2016, 18:06:46:
А если у меня будут данные вида
Код: XML
  1. 2016-12-01 00:00:01;2015-10-06 15:09:01;данные1
  2. 2016-12-02 00:00:02;2016-12-03 10:00:04;данные2
  3. 2016-12-03 00:00:03;2016-09-23 11-00-09;данные3
  4. 2016-12-04 00:00:04;2016-12-01 09:22:00;данные4
  5. 2016-12-05 00:00:05;2016-11-29 18:44:13;данные5
то для их сортировки я применю sort -t";" -k2 и получу
Код: XML
  1. 2016-12-01 00:00:01;2015-10-06 15:09:01;данные1
  2. 2016-12-03 00:00:03;2016-09-23 11-00-09;данные3
  3. 2016-12-05 00:00:05;2016-11-29 18:44:13;данные5
  4. 2016-12-04 00:00:04;2016-12-01 09:22:00;данные4
  5. 2016-12-02 00:00:02;2016-12-03 10:00:04;данные2
Но вот как выбрать из промежутка дат?

Пользователь добавил сообщение 04 Декабрь 2016, 19:16:48:
пока сам себе отвечаю, но может быть, ещё кому пригодится.
Т.к. Гугл меня ещё не забанил :) ,то по одной ссылке http://stackoverflow.com/questions/17465686/shell-script-to-extract-data-from-file-between-two-date-ranges
нашел вариант команды sort file | sed -n '/2013-06-12/,/2013-06-15/p' который будет работать, если границы промежутка совпадают со значениями, которые есть в файле input.csv (см. мой пример выше). Но мне бы хотелось, чтобы можно было скормить скрипту произвольные даты, между которыми осуществлялся бы поиск. Но уже теплее.. Может быть у кого есть идеи как допилить? Если разберусь раньше, то отпишусь.

Пользователь добавил сообщение 04 Декабрь 2016, 21:01:43:
В общем ночь, голова не соображает, получилось вот что:
Код: Bash
  1. #!/bin/bash
  2. cp /home/morhad/out1.txt /home/morhad/out2.txt
  3. echo ";"$1" 00:00:00;начало" >> /home/morhad/out2.txt
  4. echo ";"$2" 00:00:00;конец" >> /home/morhad/out2.txt
  5. #cat /home/morhad/out2.txt
  6. #let tmp="/$1 00:00:00/,/$2 00:00:00/p"
  7. sort -t";" -k2 /home/morhad/out2.txt | sed -n "/$1 00:00:00/,/$2 00:00:00/p" > /home/morhad/out3.txt
  8. sed '1,1d' /home/morhad/out3.txt > /home/morhad/out4.txt
  9. sed '$d' /home/morhad/out4.txt > /home/morhad/out3.txt
  10. cat /home/morhad/out3.txt

Если скрипту скормить две даты, то в файле out3.txt будут именно отсортированные между этими датами строки.
Но может быть есть более красивое решение, но на bash ?
Листинги и содержимое текстовых файлов следует добавлять в сообщение с помощью тега [spoiler]...[/spoiler] или [code]...[/code], либо прикреплять к сообщению в виде отдельного файла.
--Azure
« Последнее редактирование: 05 Декабрь 2016, 12:32:23 от Azure »

Оффлайн ReNzRv

  • Старожил
  • *
  • Сообщений: 1969
    • Просмотр профиля
Скрипт d.awk:
Код: awk
  1. BEGIN{
  2. FS=";"
  3. gsub("-|:"," ",start)
  4. gsub("-|:"," ",end)
  5. start=mktime(start)
  6. end=mktime(end)
  7. }{
  8. d=$1
  9. gsub("-|:"," ",d)
  10. d=mktime(d)
  11. if(d>=start && d<=end){print}
  12. }

Пример запуска:
awk -v start='2016-10-29 18:44:13' -v end='2016-11-15 18:44:13' -f d.awk файл.csv
Сортировка дат не нужна.
« Последнее редактирование: 04 Декабрь 2016, 21:36:19 от renzrv »

Оффлайн Azure

  • СуперМодератор
  • Старожил
  • *
  • Сообщений: 5661
  • elementaryOS 0.4 Loki, i3wm on Debian9
    • Просмотр профиля
mktime — команда из GNU awk. Если установлен простой (mawk) то придется использовать системный date("date +%s -d \""$1"\"") | getline d
В Линукс можно сделать ВСЁ что угодно, достаточно знать КАК !

Оффлайн ReNzRv

  • Старожил
  • *
  • Сообщений: 1969
    • Просмотр профиля
Azure,
На обычной ubuntu работает, date использовать(что ооочень замедлит скрипт) не придется.
Но если придется, то вы забыли добавть close после getline,
т.к при колличестве больше 1030 будет вот такая ошибка:
1030 2015-08-19 09:51:27
awk: командная строка:1: (FILENAME=file.csv FNR=1031) критическая ошибка: не удалось открыть канал `date "+%F %H:%M:%S" -d "@1439967107"' (Слишком много открытых файлов)

Правильно:
Код: awk
  1. BEGIN{cmd="date +%s -d \""}{cmd $1 "\"" | getline d ; close(cmd)}

И mktime не команда, а функция!
http://www.gnu.org/software/gawk/manual/gawk.html#Time-Functions

« Последнее редактирование: 05 Декабрь 2016, 14:39:47 от renzrv »

Оффлайн morhad

  • Автор темы
  • Любитель
  • *
  • Сообщений: 50
    • Просмотр профиля
спасибо. чувствую, то про awk подробнее почитать нужно :)
Я пока такой скрипт наваял, которому две даты скармливаю: ./test.sh 2016-12-01 2016-12-05
Он обрабатывает файл out1.txt, в котором данные по полям, среди которых есть и другие даты, но интересующие меня даты,
по которым хочу выборку сделать находятся в 10-ом поле.
Код: Bash
  1. #!/bin/bash
  2. cat /home/morhad/out1.txt | sed -r 's/("[^"]*"|[0-9.]+)(,|$)/\1\;/g;s/"//g' | awk -F ";" '{print}' > /home/morhad/out2.txt
  3. # Первая строка заменяет запятые-разделители полей на точку с запятой.
  4. # Обращаю внимание, что запятые могут находится внутри символьного поля, которое ограничено кавычками. Такие запятые не меняются
  5. echo ";;;;;;;;;"$1" 00:00:00;начало" >> /home/morhad/out2.txt
  6. echo ";;;;;;;;;"$2" 23:59:59;конец" >> /home/morhad/out2.txt
  7. # Две строки выше добавляют даты, по которым выборка, в конец файла out2.txt
  8. sort -t";" -k10 /home/morhad/out2.txt | sed -n "/$1 00:00:00/,/$2 23:59:59/p" > /home/morhad/out3.txt
  9. # Сортировка. Да это медленно для больших файлов. Поэтому и хочу с awk поразбираться.
  10. # А то ничего не понял в тех скриптах, которые мне написали уважаемые авторы в данной ветке на форуме.
  11. sed '1,1d' /home/morhad/out3.txt > /home/morhad/out4.txt
  12. sed '$d' /home/morhad/out4.txt > /home/morhad/out.total
  13. # Две последние строки удаляют из файла первую строчку с начальной датой и последнюю строчку с конечной датой сортировки.
  14. # Выходные данные смотрим в out.total
:) Громоздко, да?
« Последнее редактирование: 06 Декабрь 2016, 17:12:16 от Azure »

Оффлайн ReNzRv

  • Старожил
  • *
  • Сообщений: 1969
    • Просмотр профиля
Цитировать
по которым хочу выборку сделать находятся в 10-ом поле.
# Первая строка заменяет запятые-разделители полей на точку с запятой.
# Обращаю внимание, что запятые могут находится внутри символьного поля, которое ограничено кавычками. Такие запятые не меняются
Покажи пример таких строк.

Оффлайн morhad

  • Автор темы
  • Любитель
  • *
  • Сообщений: 50
    • Просмотр профиля
Например, файл /var/log/asterisk/cdr-csv/Master.csv
Ниже одна строчка из этого файла (номера телефонов изменены)
Код: Text
  1. "","4232254963","2510490","vhod_zvonki","4232254963","SIP/2510490-00002c09","SIP/405-00002c0c","Dial","SIP/403&SIP/404&SIP/405&SIP/406&SIP/407&LOCAL/boss@staff2,,tTr","2016-11-16 22:54:45","2016-11-16 22:54:58","2016-11-16 22:55:14",29,16,"ANSWERED","DOCUMENTATION","1789336765.15237",""
« Последнее редактирование: 06 Декабрь 2016, 17:11:35 от Azure »

Оффлайн ReNzRv

  • Старожил
  • *
  • Сообщений: 1969
    • Просмотр профиля
d.sh:
Код: Bash
  1. #!/bin/bash
  2.  
  3. [ $# = 3 ] || exit
  4.  
  5. s="$1"
  6. e="$2"
  7. file="$3"
  8.  
  9. awk -v start="${s} 00 00 00" -v end="${e} 23 59 59" 'BEGIN{
  10. FS=","
  11. gsub("-|:"," ",start)
  12. gsub("-|:"," ",end)
  13. start=mktime(start)
  14. end=mktime(end)
  15. }{
  16. d=$12
  17. gsub("-|:"," ",d)
  18. gsub("\"","",d)
  19. d=mktime(d)
  20. if(d>=start && d<=end){print}
  21. }' "$file"
  22.  

Пример запуска:
bash d.sh 2016-11-14 2016-11-16 файл.csv

Пользователь добавил сообщение 06 Декабрь 2016, 19:13:10:
Если нужно вывести в файл:
bash d.sh 2016-11-14 2016-11-16 файл.csv > /home/morhad/out.total
Цитировать
ничего не понял в тех скриптах, которые мне написали
Скрипт конвертирует дату начала интервала (start), дату конца интервала (end), и дату в 12м поле
каждой строки(разделитель полей - ",") в штампы в секундах (seconds since 1970-01-01 00:00:00 UTC), и если штамп даты в секудах 12го поля >= start и <= end - печатает строку.
« Последнее редактирование: 06 Декабрь 2016, 19:27:04 от renzrv »

Оффлайн morhad

  • Автор темы
  • Любитель
  • *
  • Сообщений: 50
    • Просмотр профиля
Разделитель полей у вас запятая, а ваш скрипт будет различать правильно поля, если встречаются некоторые поля, внутри которых есть запятые?

Оффлайн ReNzRv

  • Старожил
  • *
  • Сообщений: 1969
    • Просмотр профиля
Нет, но вот вариант если в окавыченых полях есть запятые:
#!/bin/bash
 
[ $# = 3 ] || exit
 
s="$1"
e="$2"
file="$3"
 
awk -v start="${s} 00 00 00" -v end="${e} 23 59 59" 'BEGIN{
FS=","
gsub("-|:"," ",start)
gsub("-|:"," ",end)
start=mktime(start)
end=mktime(end)
}{
while(match($0,"\"[^\"]*,[^\"]*\"(,|$)")){
if(prev==RSTART){break}
prev=RSTART
s=substr($0,RSTART,RLENGTH)
r=s
gsub(","," ",r)
gsub("&","(and)",r)
sub(s,r",")
}
d=$10
gsub("-|:"," ",d)
gsub("\"","",d)
d=mktime(d)
if(d>=start && d<=end){print}
}' "$file"

Запятые в окавыченых полях заменяются на пробелы, символ "&" на (and).
« Последнее редактирование: 10 Декабрь 2016, 22:33:54 от renzrv »

Оффлайн morhad

  • Автор темы
  • Любитель
  • *
  • Сообщений: 50
    • Просмотр профиля
спасибо.

 

Страница сгенерирована за 0.079 секунд. Запросов: 24.