awk 'BEGIN { while(getline < "file2" ) { codes[$1] = $2 } }
{ printf "%4s%s\n", codes[$1], substr($0, 5) }' file1
Eu tenho dois arquivos com aproximadamente 12900 e 4400 entradas, respectivamente, que desejo ingressar. Os arquivos contêm informações de localização para todas as estações observadoras terrestres do tempo em todo o mundo. O maior arquivo é atualizado quinzenalmente, e o menor, uma vez por ano, aproximadamente. Os arquivos originais podem ser encontrados aqui ( link e link ). Os arquivos que eu tenho já são manipulados por mim com alguns scripts mistos de awk, sed e bash. Eu uso os arquivos para visualizar dados usando o Pacote GEMPAK, disponível gratuitamente na Unidata. O maior arquivo funcionará com o GEMPAK, mas não apenas com sua capacidade total. Para isso é necessária uma junção.
O arquivo 1 contém informações de localização para estações de observação meteorológica, onde os primeiros 6 dígitos são o identificador exclusivo da estação. Os diferentes parâmetros (número da estação, nome da estação, código do país, longitude da latitude e elevação da estação) são definidos apenas pela sua posição na linha, ou seja, sem tabulações.
060090 AKRABERG FYR DN 6138 -666 101
060100 VAGA FLOGHAVN DN 6205 -728 88
060110 TORSHAVN DN 6201 -675 55
060120 KIRKJA DN 6231 -631 55
060130 KLAKSVIK HELIPORT DN 6221 -656 75
060160 HORNS REV A DN 5550 786 21
060170 HORNS REV B DN 5558 761 10
060190 SILSTRUP DN 5691 863 0
060210 HANSTHOLM DN 5711 858 0
060220 TYRA OEST DN 5571 480 43
060240 THISTED LUFTHAVN DN 5706 870 8
060290 GROENLANDSHAVNEN DN 5703 1005 0
060300 FLYVESTATION AALBORG DN 5708 985 13
060310 TYLSTRUP DN 5718 995 0
060320 STENHOEJ DN 5736 1033 56
060330 HIRTSHALS DN 5758 995 0
060340 SINDAL FLYVEPLADS DN 5750 1021 28
O Arquivo 2 contém o identificador exclusivo no Arquivo 1 e um segundo identificador de 4 caracteres (localizador ICAO).
060100 EKVG
060220 EKGF
060240 EKTS
060300 EKYT
060340 EKSN
060480 EKHS
060540 EKHO
060600 EKKA
060620 EKSV
060660 EKVJ
060700 EKAH
060780 EKAT
Eu quero juntar os dois arquivos, para que o arquivo resultante tenha o identificador de 4 caracteres nas primeiras 4 posições na linha, ou seja, o identificador deve substituir os 4 espaços.
060090 AKRABERG FYR DN 6138 -666 101
EKVG 060100 VAGA FLOGHAVN DN 6205 -728 88
060110 TORSHAVN DN 6201 -675 55
060120 KIRKJA DN 6231 -631 55
060130 KLAKSVIK HELIPORT DN 6221 -656 75
060160 HORNS REV A DN 5550 786 21
060170 HORNS REV B DN 5558 761 10
060190 SILSTRUP DN 5691 863 0
060210 HANSTHOLM DN 5711 858 0
EKGF 060220 TYRA OEST DN 5571 480 43
EKTS 060240 THISTED LUFTHAVN DN 5706 870 8
060290 GROENLANDSHAVNEN DN 5703 1005 0
EKYT 060300 FLYVESTATION AALBORG DN 5708 985 13
060310 TYLSTRUP DN 5718 995 0
060320 STENHOEJ DN 5736 1033 56
060330 HIRTSHALS DN 5758 995 0
EKSN 060340 SINDAL FLYVEPLADS DN 5750 1021 28
É possível realizar essa tarefa com algum script bash e / ou awk?
Alguns de nós queriam ver se poderíamos resolver esse problema usando join
apenas. Esta é minha tentativa de fazer isso. Como funciona parcialmente @Terdon me deve um jantar 8 -).
$ join -a1 -1 1 -2 1 -o 2.2 1.1 1.2 1.3 1.4 1.5 1.6 1.7 -e "N/A" \
<(sort file1) <(sort file2)
$ join -a1 -1 1 -2 1 -o 2.2 1.1 1.2 1.3 1.4 1.5 1.6 1.7 -e "N/A" <(sort file1) <(sort file2) | column -t
N/A 060090 AKRABERG FYR DN 6138 -666 101
EKVG 060100 VAGA FLOGHAVN DN 6205 -728 88
N/A 060110 TORSHAVN DN 6201 -675 55 N/A
N/A 060120 KIRKJA DN 6231 -631 55 N/A
N/A 060130 KLAKSVIK HELIPORT DN 6221 -656 75
N/A 060160 HORNS REV A DN 5550 786
N/A 060170 HORNS REV B DN 5558 761
N/A 060190 SILSTRUP DN 5691 863 0 N/A
N/A 060210 HANSTHOLM DN 5711 858 0 N/A
EKGF 060220 TYRA OEST DN 5571 480 43
EKTS 060240 THISTED LUFTHAVN DN 5706 870 8
N/A 060290 GROENLANDSHAVNEN DN 5703 1005 0 N/A
EKYT 060300 FLYVESTATION AALBORG DN 5708 985 13
N/A 060310 TYLSTRUP DN 5718 995 0 N/A
N/A 060320 STENHOEJ DN 5736 1033 56 N/A
N/A 060330 HIRTSHALS DN 5758 995 0 N/A
EKSN 060340 SINDAL FLYVEPLADS DN 5750 1021 28
O acima está fazendo uso de praticamente todas as opções disponíveis para join
, que diz ao meu instinto que estamos usando errado, como em algum tipo de caminho de Frankenstein, mas todos nós estamos aprendendo aqui, então tudo bem. .. eu acho.
O comutador -a1
diz ao join para incluir quaisquer linhas que não tenham uma correspondência correspondente do arquivo2 no arquivo1. Então é isso que está fazendo com que essas linhas sejam exibidas:
N/A 060330 HIRTSHALS DN 5758 995 0 N/A
Os -1 1
e -2 1
estão dizendo quais colunas devem unir as linhas dos dois arquivos, principalmente suas primeiras colunas. O -o ...
está dizendo quais colunas dos dois arquivos serão exibidos e em qual ordem.
O -e "N/A"
diz usar a string "N / A" como um valor de espaço reservado para imprimir em campos que são considerados vazios por join
.
Os últimos 2 argumentos estão alimentando os 2 arquivos, file1
& file2
conforme ordenado no comando de união.
Por favor, seja gentil, já que este é um trabalho em andamento e estamos tentando demonstrar como alguém poderia resolver esse tipo de problema usando o comando join
, já que este parece ser o tipo de problema para o qual foi feito .
3a coluna
O principal deles é como lidar com a 3ª coluna, já que é uma mistura de valores de 1 palavra e 2 palavras. Isso parece ser um grande obstáculo para join
e eu não consigo descobrir uma maneira de contornar isso. Qualquer orientação seria apreciada.
Espaçamento
Todo o espaçamento original é perdido com join
e também não vejo uma maneira de mantê-lo. Portanto, join
pode não ser o caminho certo para lidar com esses tipos de problemas, afinal.
Parece funcionar?
Depois de muita flexão com a linha de comando, a solução geral está lá, então parece que pode funcionar pelo menos parcialmente, então isso poderia ser usado no núcleo de uma solução e usar outras ferramentas, como awk
e sed
para limpá-lo. Isso levanta a questão: "Se você está limpando com awk
& sed
de qualquer forma, então você pode simplesmente usá-los diretamente?".
Isso deve ser possível usando join
, mas não consigo descobrir como fazer com que ele imprima espaços e esvazie os campos corretamente. Enfim, esse pequeno script Perl fará o seguinte:
#!/usr/bin/env perl
## Open file2, the one that contains the codes
## it is expected to be the 1st argument given to the script.
open($a,"$ARGV[0]");
## Read the number<=>code pairs into a hash (an associative array)
## called 'k'
while (<$a>) {
chomp; @f=split(/\s+/); $k{$f[0]}=$f[1];
}
## Open file1, the one that contains the data
## it is expected to be the 2nd argument given to the script.
open($b,"$ARGV[1]");
## Go through the file
while (<$b>) {
## Split each line at white space into the array @f
@f=split(/\s+/);
## $f[1] is the 6 digit number that defines the different stations.
## If this number has an entry in the hash %k, if it was found
## in file2, replace the first 4 spaces with its value from the hash.
s/^\s{4}/$k{$f[1]}/ if defined($k{$f[1]});
## Print each line of the file
print;
}
Salve isso como foo.pl
e execute da seguinte forma:
$ perl foo.pl file2 file1
060090 AKRABERG FYR DN 6138 -666 101
EKVG 060100 VAGA FLOGHAVN DN 6205 -728 88
060110 TORSHAVN DN 6201 -675 55
060120 KIRKJA DN 6231 -631 55
060130 KLAKSVIK HELIPORT DN 6221 -656 75
060160 HORNS REV A DN 5550 786 21
060170 HORNS REV B DN 5558 761 10
060190 SILSTRUP DN 5691 863 0
060210 HANSTHOLM DN 5711 858 0
EKGF 060220 TYRA OEST DN 5571 480 43
EKTS 060240 THISTED LUFTHAVN DN 5706 870 8
060290 GROENLANDSHAVNEN DN 5703 1005 0
EKYT 060300 FLYVESTATION AALBORG DN 5708 985 13
060310 TYLSTRUP DN 5718 995 0
060320 STENHOEJ DN 5736 1033 56
060330 HIRTSHALS DN 5758 995 0
EKSN 060340 SINDAL FLYVEPLADS DN 5750 1021 28
Bash fará.
#!/usr/bin/env bash
# ### create a psuedo hash of icao locator id's
# read each line into an array
while read -a line; do
# set icao_nnnnnn variable to the value
declare "icao_${line[0]}"=${line[1]}
done <file2
# ### match up icao id's from file1
# read in file line at a time
while IFS=$'\n' read line; do
# split the line into array
read -a arr <<< "$line"
# if the icao_nnnnnn variable exists, it will print out
var="icao_${arr[0]}"
printf "%-8s %s\n" "${!var}" "$line"
done <file1
Veja esta resposta SO para os detalhes do que está acontecendo com o "hash" Bash 4 suporta nativamente array associativo, mas isso deve funcionar em 3 + 4 (talvez 2?)
Talvez seja necessário deixar a linha do arquivo 1 para obter sua formatação.
Aqui está uma maneira simples de fazer isso com join
(+ mais algumas ferramentas) e preservar o espaçamento. Os dois arquivos parecem ser classificados por número de estação, portanto, nenhuma classificação adicional é necessária:
join -j1 -a1 -o 2.2 -e " " file1 file2 | paste -d' ' - <(cut -c6- file1)
A parte antes do pipe é muito semelhante ao slm usado em sua resposta Eu não vou passar por isso novamente. A única diferença é que estou usando -e " "
- uma cadeia de quatro espaços como um substituto para campos de entrada ausentes e -o 2.2
para produzir apenas o segundo campo do arquivo2
Portanto, join -j1 -a1 -o 2.2 -e " " file1 file2
produz uma coluna com quatro caracteres (não é visível abaixo, mas não há nada após EK ** e as linhas vazias são, na verdade, quatro espaços):
EKVG EKGF EKTS EKYT EKSN
nós então paste
this (usando espaço como um delimitador) para o arquivo1 do qual nós cut
os primeiros 5 caracteres | paste -d' ' - <(cut -c6- file1)
Resultado final:
060090 AKRABERG FYR DN 6138 -666 101
EKVG 060100 VAGA FLOGHAVN DN 6205 -728 88
060110 TORSHAVN DN 6201 -675 55
060120 KIRKJA DN 6231 -631 55
060130 KLAKSVIK HELIPORT DN 6221 -656 75
060160 HORNS REV A DN 5550 786 21
060170 HORNS REV B DN 5558 761 10
060190 SILSTRUP DN 5691 863 0
060210 HANSTHOLM DN 5711 858 0
EKGF 060220 TYRA OEST DN 5571 480 43
EKTS 060240 THISTED LUFTHAVN DN 5706 870 8
060290 GROENLANDSHAVNEN DN 5703 1005 0
EKYT 060300 FLYVESTATION AALBORG DN 5708 985 13
060310 TYLSTRUP DN 5718 995 0
060320 STENHOEJ DN 5736 1033 56
060330 HIRTSHALS DN 5758 995 0
EKSN 060340 SINDAL FLYVEPLADS DN 5750 1021 28