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, awk
as 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 strptime
função adequada, enquanto mesmo com GNU awk mktime
você precisa analisar e montar manualmente o datespec
argumento.
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 --csv
para --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 universe
repositó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)}'