Bash script / awk para entrada e saída de um arquivo CSV

Sep 28 2020

Estou tendo problemas para criar um script bash / awk / sed que pegaria um arquivo CSV separado por vírgulas de três colunas (nome, sobrenome, data de nascimento) e gera outro arquivo CSV que tem as mesmas colunas de entrada com um coluna adicional que mostra a diferença entre a data atual e a data de nascimento em anos.

$ yourscript <input CSV file> <output CSV file>

input.csv pode ter a seguinte aparência:

bob,wag,06/13/1958
ashley,hay,01/23/1983
evan,bert,09/11/1972

output.csv deve ter a seguinte aparência:

bob,wag,06/13/1958,62
ashley,hay,01/23/1983,37
evan,bert,09/11/1972,48

Respostas

3 Cbhihe Sep 28 2020 at 14:46
$ cat data
bob,wag,06/13/1958
ashley,hay,01/23/1983
evan,bert,09/11/1972

Para produzir em um arquivo denominado output-filee exibi-lo em STDOUT ao mesmo tempo:

$ awk -v year=$(\date +%Y) 'BEGIN{FS="/"} {print $0 "," year-$3}' data | tee output-file
bob,wag,06/13/1958,62
ashley,hay,01/23/1983,37
evan,bert,09/11/1972,48

Ou apenas para o mesmo arquivo:

$ awk -v year=$(\date +%Y) 'BEGIN{FS="/"} {print $0 "," year-$3}' data > output-file
2 Cbhihe Sep 30 2020 at 18:04

Para realizar cálculos de tempo mais precisos, você pode usar gawkas funções de tempo e string (por sugestão de @AdminBee). Usando dados de entrada como:

$ cat data
bob,wag,06/13/1958
ashley,hay,01/23/1983
evan,bert,09/11/1972

Você pode obter uma diferença de tempo em dias entre agora e a data mostrada em cada linha, com:

$ awk -F, 'BEGIN{today=systime()} 
           {print $0 "," int((today-mktime(substr($3,7,4)" "substr($3,1,2)" "substr($3,4,2)" "00" "00" "00))/(3600*24))}' \
           data | tee output-file

 bob,wag,06/13/1958,22755
 ashley,hay,01/23/1983,13765
 evan,bert,09/11/1972,17551

O snippet:

int((today-mktime(substr($3,7,4)" "substr($3,1,2)" "substr($3,4,2)" "00" "00" "00))/(3600*24))

faz três coisas básicas para cada linha do arquivo de entrada:

  • calcula o tempo decorrido (em segundos) desde 01/01/1970 00:00:00 UTC (em sistemas POSIX), sem contar os segundos bissextos, com mktime(substr($3,7,4)" "substr($3,1,2)" "substr($3,4,2)" "00" "00" "00)
  • ele calcula a diferença de tempo entre a quantidade acima e a variável today, que contém o número de segundos decorridos no momento da execução desde 01/01/1970 00:00:00 UTC.
  • ele divide a diferença de tempo em segundos por 3600 * 24 para obter o mesmo em dias, e considera apenas a parte inteira do resultado, para obter dias inteiros int().

Você pode brincar com isso para obter a diferença de tempo em segundos, minutos, horas de acordo com sua necessidade. HTH

MaheshK. Sep 28 2020 at 11:49

Para obter a diferença de data, você pode usar esta pequena função bash

Use os seguintes números para suas necessidades

  • #Para obter a diferença de dias : 86400
  • #Para obter a diferença de anos : 31536000

Função Bash

datediff() {
  current_date=$(date -d "$1" +%s)
  birth_date=$(date -d "$2" +%s)
  echo $(( (current_date - birth_date) / 31536000)) Years
}

Uso:

datediff '9/28/2020' '1/1/1999'

Resultado:

 21 years
MarcinŚlusarz Jan 09 2021 at 09:58

Dependendo do que você realmente deseja:

anos de diferença:

(echo "firstname,lastname,d"; cat input.csv) | csv-sqlite \
 "select *, strftime('%Y', 'now') - substr(d, 7, 4) as year_diff
  from input" | csv-header --remove

era:

(echo "firstname,lastname,d"; cat input.csv) | csv-sqlite
  "select *, strftime('%Y', 'now') - substr(d, 7, 4) -
             case when strftime('%m%d', 'now') >= (substr(d, 1, 2) || substr(d, 4, 2))
             then 0 else 1 end as age
  from input" | csv-header --remove

csv-sqlite e csv-header são de csv-nix-tools