Как извлечь столбцы из файла CSV, обработать и создать файл CSV на основе результата извлечения и обработки?

Это фрагмент исходного CSV-файла.

%status,date,job,project,start,end,description
%
//,18.03.2021,sib,sib-dede,07:00,15:00,dede-mongo
%
//,11.06.2021,sib,sib-dede,07:00,15:00,dede-mongo
%
//,24.06.2021,sib,sib-dede,07:00,15:00,dede-mongo
%
?,02.08.2021,sib,sib-accounting,14:35,16:35,business-plan
%
?,13.10.2021,sb,sb-accounting,11:30,12:00,e-mail-pump

Мне нравится извлекать из исходного CSV-файла время начала в столбце 5 и время окончания в столбце 6.

Далее, в зависимости от времени начала и окончания, я предпочитаю рассчитывать продолжительность в часах или минутах (не обращайте внимания).

В конце концов, мне нравится брать исходный файл CSV, вставлять новый столбец между существующими столбцами 6 и 7 с обработанной длительностью и сохранять это добавление в результирующем файле CSV.

Есть ли у кого-нибудь идеи, как решить эту проблему в командной строке GNU Debian Linux?

Я знаю, что могу cut определённые столбцы из файла CSV вот так.

cut -d, -f5,6 < ./source.csv > ./result.csv

Однако мне все еще не хватает обработки длительности и создания результирующего файла CVS.

PS: Я отдаю предпочтение Bash.

PPS: Есть связанные вопросы, такие как этот, но я не нашел ни одного близкого к этому вопросу.

🤔 А знаете ли вы, что...
Bash поддерживает механизмы обработки сигналов для управления выполнением скриптов.


57
2

Ответы:

Решено

Я отдаю предпочтение Bash

Но вы можете сделать все это за один раз, например. используя mktime из GNU Awk (при условии, что знаки % (или последующие символы новой строки) в вашем образце являются просто опечаткой и что время в столбце 6 больше, чем время в столбце 5, в противном случае результат будет отрицательным):

awk -F , -v OFS=, '{
  $8=$7; $7=(
    mktime(sprintf("0 0 0 %d %d 0", substr($6,1,2), substr($6,4,2))) - 
    mktime(sprintf("0 0 0 %d %d 0", substr($5,1,2), substr($5,4,2)))
  ) / 60; print
}' source.csv

Предполагая, что:

  1. Все временные метки относятся к одному и тому же дню, поскольку у вас нет никаких указаний на дату во входных данных, и поэтому нет надежного способа обработки продолжительности, которая может превышать 24 часа.
  2. Временные метки представляют собой стандартное время, поэтому нет необходимости в изменении часа для перехода на летнее время.
  3. У вас действительно есть строки, состоящие всего из %, за которыми следуют строки, начинающиеся с // или ? во входных данных, и
  4. Вы также хотите, чтобы строка заголовка была изменена и включала заголовок, например diff, для добавляемого столбца.

затем используя любой awk:

$ awk '
    BEGIN { FS=OFS = "," }
    NF > 1 {
        if ( NR == 1 ) {
            diff = "diff"
        }
        else {
            split($5, b, ":")
            split($6, e, ":")
            beg = b[1]*60 + b[2]
            end = e[1]*60 + e[2]
            diff = end - beg
        }
        $6 = $6 OFS diff
        print
    }
' file
%status,date,job,project,start,end,diff,description
//,18.03.2021,sib,sib-dede,07:00,15:00,480,dede-mongo
//,11.06.2021,sib,sib-dede,07:00,15:00,480,dede-mongo
//,24.06.2021,sib,sib-dede,07:00,15:00,480,dede-mongo
?,02.08.2021,sib,sib-accounting,14:35,16:35,120,business-plan
?,13.10.2021,sb,sb-accounting,11:30,12:00,30,e-mail-pump

Не нужно сначала запускать grep — передача grep в awk является антипаттерном, см. https://porkmail.org/era/unix/award#grep.


Интересные вопросы для изучения