Como manipular campos específicos?

Como manipular campos específicos?

Eu tenho um arquivo csv. Seus números de linha são no máximo 25.

Títulossão tempo (UTC) (1º campo), latitude (2º campo), longitude (3º campo), profundidade (4º campo), mag (5º campo), local (14º campo) e assim por diante

Dados de amostra

2019-12-10T21:58:28.816Z 35.488 26.4157 57.32 5.4 35km NNE of Palaikastron, Greece


2019-12-11T11:54:27.670Z 18.6158 -67.2838 85 2.85 23km NW of San Antonio, Puerto Rico

Primeiro, quero inserir o título, incluindo a variável interna fieldwith. Em segundo lugar, quero converterhorário (utc)paraUTC+03:00e dividido em data e hora dos títulos e alterar seu formato de data. Terceiro, quero extrair correspondências entre 'de'palavra evírgulaantes do nome do país para o 14º campo.

Saída desejada como títulos:

Date Time Latitude Longitude Depth Mag Place

Saída desejada:

11.12.2019 00:58:28 35.488 26.4157 57.32 5.4 Palaikastron


11.12.2019 14:54:27 18.6158 -67.2838 85 2.85 San Antonio
time,latitude,longitude,depth,mag,magType,nst,gap,dmin,rms,net,id,updated,place,type,horizontalError,depthError,magError,magNst,status,locationSource,magSource
2019-12-06T13:04:46.931Z,-15.2838,-175.1193,10,6,mww,,50,3.512,0.81,us,us60006n19,2019-12-07T13:11:48.228Z,"164km WNW of Hihifo, Tonga",earthquake,8.4,1.9,0.08,15,reviewed,us,us
2019-12-04T20:10:03.614Z,-19.0515,169.5628,266,6,mww,,21,2.812,0.82,us,us60006m2j,2019-12-05T23:44:01.300Z,"63km NNE of Isangel, Vanuatu",earthquake,7.6,1.9,0.037,71,reviewed,us,us
2019-12-03T08:46:36.374Z,-18.5597,-70.6504,32.44,6,mww,,112,0.31,1.4,us,us70006fh7,2019-12-05T08:07:29.617Z,"37km WSW of Arica, Chile",earthquake,6.2,7.8,0.069,20,reviewed,us,us
2019-12-02T05:01:54.693Z,51.3218,-178.2425,27.33,6,mww,,104,0.862,0.97,us,us70006f6d,2019-12-07T02:09:55.119Z,"60km E of Amatignak Island, Alaska",earthquake,6.7,4.2,0.066,22,reviewed,us,us
2019-11-27T07:23:42.552Z,35.7272,23.2673,71.76,6,mww,,23,1.394,1.16,us,us70006dlt,2019-12-03T23:18:27.456Z,"41km NW of Platanos, Greece",earthquake,5.8,5.4,0.046,46,reviewed,us,us
2019-11-26T02:54:12.594Z,41.5112,19.5151,20,6.4,mww,,17,0.937,0.58,us,us70006d0m,2019-12-09T15:46:11.689Z,"16km WSW of Mamurras, Albania",earthquake,3.5,1.8,0.037,72,reviewed,us,us
2019-11-24T00:54:01.052Z,51.3809,-175.5108,20,6.3,mww,,22,0.658,0.95,us,us70006cb6,2019-12-10T01:04:03.731Z,"96km SE of Adak, Alaska",earthquake,3.9,1.8,0.05,38,reviewed,us,us
2019-11-23T12:11:16.261Z,1.6286,132.7854,10,6.1,mww,,38,4.549,1.1,us,us70006c6w,2019-11-25T21:00:33.040Z,"Papua region, Indonesia",earthquake,7.8,1.8,0.061,26,reviewed,us,us
2019-11-20T23:50:43.955Z,19.4533,101.3558,10,6.2,mww,,15,2.366,0.62,us,us70006ara,2019-12-04T05:52:37.313Z,"32km ESE of Chaloem Phra Kiat, Thailand",earthquake,6.4,1.7,0.049,40,reviewed,us,us

Se consigo ter sucesso em uma coisa, não faço outras.É um desafio para mim. Guie-me, por favor. Por um lado, tenho dificuldade em entender como usá awk-lo. Por outro lado, awkas funções de tempo são muito úteis. Estou tão confuso agora. Não importa o que eu tenha tentado, não consegui.

Responder1

Embora aprender awk seja um objetivo admirável, ele não possui nenhum mecanismo integrado para analisar arquivos CSV verdadeiros (em particular, campos que podem conter delimitadores com escape ou entre aspas) - e as funções de tempo são específicas do GNU e não portáveis.

Por estas razões você pode considerar usar Perl (com seuTexto::CSVmódulo), Python - ou meu favorito atual para esse tipo de coisa,Moleiro. Além de fornecer análise CSV verdadeira, eles também fornecem uma strptimefunção adequada, enquanto mesmo com GNU awk mktimevocê precisa analisar e montar manualmente o datespecargumento.

No Miller, por exemplo, você poderia fazer o seguinte:

mlr --csv \
  put -S '
    s = strptime($time,"%Y-%m-%dT%H:%M:%SZ") + 3*3600; 
    $date = strftime(s,"%d.%m.%Y"); 
    $time = strftime(s,"%H:%M:%S"); 
    $place =~ "(.* of |)([^,]*),(.*)$" { $place = "\2" }
  ' then cut -o -f date,time,latitude,longitude,depth,mag,place input.csv

Se você quiser colunas de saída separadas por espaços em branco, mude --csvpara --icsv --opprint(saída tabular "bem impressa" - com cabeçalhos) ou --icsv --onidx(saída simples separada por espaços).

Ex.

$ mlr --icsv --opprint   put -S '
    s = strptime($time,"%Y-%m-%dT%H:%M:%SZ") + 3*3600; 
    $date = strftime(s,"%d.%m.%Y"); 
    $time = strftime(s,"%H:%M:%S"); 
    $place =~ "(.* of |)([^,]*),(.*)$" { $place = "\2" }
  ' then cut -o -f date,time,latitude,longitude,depth,mag,place input.csv
date       time     latitude longitude depth mag place
06.12.2019 16:04:46 -15.2838 -175.1193 10    6   Hihifo
04.12.2019 23:10:03 -19.0515 169.5628  266   6   Isangel
03.12.2019 11:46:36 -18.5597 -70.6504  32.44 6   Arica
02.12.2019 08:01:54 51.3218  -178.2425 27.33 6   Amatignak Island
27.11.2019 10:23:42 35.7272  23.2673   71.76 6   Platanos
26.11.2019 05:54:12 41.5112  19.5151   20    6.4 Mamurras
24.11.2019 03:54:01 51.3809  -175.5108 20    6.3 Adak
23.11.2019 15:11:16 1.6286   132.7854  10    6.1 Papua region
21.11.2019 02:50:43 19.4533  101.3558  10    6.2 Chaloem Phra Kiat

Miller está disponível no universerepositório do Ubuntu.

Responder2

Primeiro, você precisará pré-processar a entrada CSV para lidar melhor com a vírgula incorporada. Em seguida, divida o AWK em partes funcionais.

$ cat preprocess.sed
#!/bin/sed -f
:start   # loop back to here
/"/{  # for any line that has a double quote
  h   # copy to the hold buffer
  s/[^"]*"\([^"]*\).*/\1/  # what is between the first pair of dquotes
  s/,/@@/g    # replace comma with '@@'
  G   # append the hold buffer to the pattern buffer
      # so we get what was in dqoutes followed by a newline followed by the
      # original line
  s/\(.*\)\n\([^"]*\)"\([^"]*\)"\(.*\)/\2\1\4/
      # replace the unquoted part with what was there
  t start   # go back to 'start'
}

Isso substituirá o ".*,.*"por .*@@.*, o que tornará mais fácil para o AWK.

Para alterar apenas a data para um novo fuso horário, substitua a primeira linha:

$ cat change_date.sh
#!/bin/sh
userTZ="${1:-UTC+3}"
sed 's/,/ /' |
    while read datestr rest; do
        if [ "${datestr}" = time ]; then
            newdate="${datestr}"
        else
            newdate=$(TZ=${userTZ} date -d "${datestr}" "+%d %m %Y %H:%M:%S")
        fi
        echo "${newdate}:${rest}"

    done

O script AWK ficaria assim:

$ cat reformat.awk
#!/bin/awk  -f
BEGIN {IFS=","}  # comma separated fields
NR==1 {print; next;}  # print the header and do nothing more with it
{   # get just the "town" from the place field
    sub(/.* of /,"",$14)  # strip up to the " of "
    sub(/@@ .*/,"",$14)   # strip after the embedded comma (now '@@')
}
{
    printf("%s %8.3f %8.3f %8.3fs %8.3f %s\n", $1, $2, $3, $4, $5, $14)
}

Certifique-se de que ambos sejam executáveis ​​​​e executadospreprocess.sed sample.csv | change_date.sh | reformat.awk

Ou em uma linha:

sed ':start;/"/{;h;s/[^"]*"\([^"]*\).*/\1/;s/,/@@/g;G;s/\(.*\)\n\([^"]*\)"\([^"]*\)"\(.*\)/\2\1\4/;t start;};s/,/ /' test.csv | while read datestr rest; do if [ "$datestr" = "time" ]; then newdate="${datestr}"; else newdate=$(TZ=UTC+3 date -d "$datestr" "+%d %m %Y %H:%M:%S"); fi; echo "${newdate},${rest}"; done | awk -F, 'NR==1 {print;next} {sub(/.* of /,"",$14);sub(/@@ .*/,"",$14)} {printf("%s %8.3f %8.3f %8.3fs %8.3f %s\n", $1, $2, $3, $4, $5, $14)}'

informação relacionada