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


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

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

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

Оффлайн morhad

  • Автор темы
  • Любитель
  • *
  • Сообщений: 50
    • Просмотр профиля
Вот если бы мне нужно было в csv файле, например, input.csv с датами вида 2016-12-04 17:46:04 отсортировать строки по возрастанию дат, то я бы просто применил команду sort. А как мне выбрать строки в промежутке от заданных мной дат?
Допустим в файле input.csv
Код: (xml) [Выделить]
2015-10-06 15:09:01;данные1
2016-12-03 10:00:04;данные2
2016-09-23 11-00-09;данные3
2016-12-01 09:22:00;данные4
2016-11-29 18:44:13;данные5
...
а мне нужно выбрать строки с датами в промежутке 2016-11-28 00:00:00 до 2016-12:05 00:00:00
Ну, наверное, я сначала бы написал команду sort input.csv и получил бы отсортированный выход вида:
Код: (xml) [Выделить]
2015-10-06 15:09:01;данные1
2016-09-23 11-00-09;данные3
2016-11-29 18:44:13;данные5
2016-12-01 09:22:00;данные4
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) [Выделить]
2016-12-01 00:00:01;2015-10-06 15:09:01;данные1
2016-12-02 00:00:02;2016-12-03 10:00:04;данные2
2016-12-03 00:00:03;2016-09-23 11-00-09;данные3
2016-12-04 00:00:04;2016-12-01 09:22:00;данные4
2016-12-05 00:00:05;2016-11-29 18:44:13;данные5
то для их сортировки я применю sort -t";" -k2 и получу
Код: (xml) [Выделить]
2016-12-01 00:00:01;2015-10-06 15:09:01;данные1
2016-12-03 00:00:03;2016-09-23 11-00-09;данные3
2016-12-05 00:00:05;2016-11-29 18:44:13;данные5
2016-12-04 00:00:04;2016-12-01 09:22:00;данные4
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) [Выделить]
#!/bin/bash
cp /home/morhad/out1.txt /home/morhad/out2.txt
echo ";"$1" 00:00:00;начало" >> /home/morhad/out2.txt
echo ";"$2" 00:00:00;конец" >> /home/morhad/out2.txt
#cat /home/morhad/out2.txt
#let tmp="/$1 00:00:00/,/$2 00:00:00/p"
sort -t";" -k2 /home/morhad/out2.txt | sed -n "/$1 00:00:00/,/$2 00:00:00/p" > /home/morhad/out3.txt
sed '1,1d' /home/morhad/out3.txt > /home/morhad/out4.txt
sed '$d' /home/morhad/out4.txt > /home/morhad/out3.txt
cat /home/morhad/out3.txt

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

Оффлайн ReNzRv

  • Старожил
  • *
  • Сообщений: 2648
    • Просмотр профиля
Скрипт d.awk:
Код: (awk) [Выделить]
BEGIN{
FS=";"
gsub("-|:"," ",start)
gsub("-|:"," ",end)
start=mktime(start)
end=mktime(end)
}{
d=$1
gsub("-|:"," ",d)
d=mktime(d)
if(d>=start && d<=end){print}
}

Пример запуска:
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

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

Оффлайн ReNzRv

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

Оффлайн ReNzRv

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

Оффлайн morhad

  • Автор темы
  • Любитель
  • *
  • Сообщений: 50
    • Просмотр профиля
Например, файл /var/log/asterisk/cdr-csv/Master.csv
Ниже одна строчка из этого файла (номера телефонов изменены)
Код: (csv) [Выделить]
"","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

  • Старожил
  • *
  • Сообщений: 2648
    • Просмотр профиля
d.sh:
Код: (bash) [Выделить]
#!/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)
}{
d=$12
gsub("-|:"," ",d)
gsub("\"","",d)
d=mktime(d)
if(d>=start && d<=end){print}
}' "$file"

Пример запуска:
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

  • Старожил
  • *
  • Сообщений: 2648
    • Просмотр профиля
Нет, но вот вариант если в окавыченых полях есть запятые:
#!/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.056 секунд. Запросов: 23.