Eu tenho isso file.txt.Z
que contém isso:
AK2*856*1036~AK3*TD1*4**~AK4*2**1*~AK4*7**1*~AK3*TD5*5**~AK4*3**6*2~AK3*REF*6**~AK4*2**1*~AK3*REF*7**~AK4*2**1*~AK3*REF*8**~AK4*2**1*~AK3*DTM*9**~AK4*2**4*20~AK4*2**4*20~AK3*CTT*12**7~AK5*R
AK2*856*1037~AK3*HL*92**~AK4*3**7*O~AK5*R~AK9*R*2*2*0~SE*25*0001~GE*1*211582~IEA*1*000211582
Cada registro consiste em vários campos começando com um cabeçalho (geralmente AK
com um número), separados por ~
. Se você substituir o ~
por uma quebra de linha recuada, ele lerá:
AK2*856*1036
AK3*TD1*4**
AK4*2**1*
AK4*7**1*
AK3*TD5*5**
AK4*3**6*2
AK3*REF*6**
AK4*2**1*
AK3*REF*7**
AK4*2**1*
AK3*REF*8**
AK4*2**1*
AK3*DTM*9**
AK4*2**4*20
AK4*2**4*20
AK3*CTT*12**7
AK5*R
AK2*856*1037
AK3*HL*92**
AK4*3**7*O
AK5*R
AK9*R*2*2*0
SE*25*0001
GE*1*211582
IEA*1*000211582
Cada campo possui subcampos separados por *
. Por exemplo, o subcampo AK201
é o primeiro campo após um AK2
cabeçalho, portanto, é 856
para as linhas de exemplo.
Como você pode ver, há 2 linhas com uma string inicial de AK2
. Isso é como um cabeçalho de linha ou, como o chamamos, cabeçalho de segmento. Existem dois cabeçalhos de segmento em file.txt.Z
. O que eu quero é obter esses dados de cada cabeçalho de segmento em ordem:
Dados Necessários:
- AK202 (segundo campo após o
AK2
cabeçalho) -AK2*856*this_numeric_value
antes do asterisco ou~
. - AK301 (primeiro campo após o
AK3
cabeçalho) -~AK3*this_string_value
antes do*
ou~
. - AK502 (segundo campo após o
AK5
cabeçalho) -~AK5*some_string_value*this_numeric_value
antes do*
ou~
. - AK401 (primeiro campo após o
AK4
cabeçalho) -~AK4*this_numeric_value
antes do*
ou~
. - Cada valor numérico de
AK4
ouAK5
campo deve ter sempre pelo menos 2 dígitos. por exemplo AK502 = 2; AK502 = 02 ou AK401 = 9; AK401 = 09. - Se não houver nenhum
AK3
campo, não imprima nada. (Já tenho um script para isso) - Se uma linha contiver mais de uma sequência AK3-AK5-AK4, elas devem ser concatenadas com um espaço em branco
- Se o
AK5
campo faltar após oAK3
campo, procure oAK4
campo em seu lugar. - Se nem um
AK4
nem umAK5
campo estiverem presentes após oAK3
campo, imprima apenas o AK301 (primeiro campo após o cabeçalho AK3). - Se houver mais de um
AK4
campo após umAK3
campo, concatene as sequências AK502-AK401 por vírgulas
Resultado:
GS: 1036 - TD102,07 TD503 REF02 DTM02,02 CTT
GS: 1037 - HL03
Como fazer isso? Apenas me pergunte se você está confuso com a minha pergunta.
Editar: Este é o meu código: isso está dentro de um loop while
while read FILE
do
AK2=`zgrep -oP 'AK2.[\w\s\d]*.\K[\w\s\d]*' < $FILE`
AK3=`zgrep -oP 'AK3.\K[\w\s\d]*' < $FILE`
AK5=`zgrep -oP 'AK5.[\w\s\d]*.\K[\w\s\d]' < $FILE`
AK5_ERROR=`if [[ $AK5 =~ ^[0-9]+$ ]]; then printf "%02d" $AK5 2> /dev/null; else 2> /dev/null; fi`
AK4=`zgrep -oP 'AK4.\K[\w\s\d]*' < $FILE`
AK4_ERROR=`if [[ $AK4 =~ ^[0-9]+$ ]]; then printf "%02d" $AK4 2> /dev/null; else 2> /dev/null; fi`
if [[ $AK3 ]]
then
if $AK5 2> /dev/null
then
echo "GS: $AK2 - $AK3$AK4_ERROR"
else
echo "GS: $AK2 - $AK3$AK5_ERROR"
fi
else
echo "Errors are not specified in the file."
fi
done < file.txt.Z
O problema com meu código original é que ele não concatena $AK3
e, $AK5
ou $AK4
.
O script perl a seguir produz sua amostra de saída exatamente quando recebe sua amostra de entrada.
Pode não funcionar exatamente como você deseja em seu arquivo de dados real, mas não está sendo apresentado como uma solução de trabalho completa. É apresentado como uma base para começar a trabalhar - brinque com o script, mexa com ele, quebre, conserte, mude para fazer o que quiser.
Está, sem dúvida, longe de ser ideal, mas seria difícil melhorá-lo muito sem um conhecimento muito mais detalhado / melhor explicação de seus dados de entrada e saída necessária.
Ele processa cada linha de entrada (também conhecido como "registro" ou "segmento" usando sua terminologia) e cria uma string a ser impressa após o processamento do registro. Cada linha de saída é construída de acordo com suas especificações na seção Dados necessários de sua pergunta.
Salvei este script
mysteryprocess.pl
porque não consegui pensar em um nome mais apropriado. Em seguida, executei-o com seus dados de amostra (em um arquivo chamadoinput
):saída de exemplo:
Essa coisa de "REF02 REF03 REF02" me incomodou, então aqui está outra versão. Este usa um array e um hash (
@groups
e%groups
) para construir a linha de saída, e outro hash (%gseen
) para evitar dupes dentro de um registro lembrando valores que já vimos e incluímos na saída.Os dados dos grupos são armazenados em
%groups
, mas os hashes não são ordenados emperl
, portanto, a@groups
matriz é usada para lembrar a ordem em que vimos um grupo específico pela primeira vez.BTW,
%groups
provavelmente deve ser um hash-of-arrays também conhecido como HoA (ou seja, um hash que contém um array em cada elemento), o que evitaria a necessidade de limpeza$output
antes de imprimi-lo (usando ajoin()
função perl em vez de simplesmente acrescentar uma vírgula e o novo valor para as strings). Mas acho que esse script já é complicado o suficiente para um novato em perl entender.Com a seguinte entrada
A saída agora é:
Notas:
DTM02,02
também foi recolhido em apenasDTM02
. A eliminação do dupe acontece para tudo agora.Não tenho certeza se alguma dessas alterações é o que você deseja.
ps: se você não tiver
perl
instalado, esse código seria traduzido facilmente paraawk
. É um algoritmo muito simples (até simplista) direto.outra tentativa, para mostrar uma versão awk, como sugerido por cas. Provavelmente pode ser feito de maneira muito mais organizada, mas foi uma experiência de aprendizado de qualquer maneira.
simplesmente dividindo inicialmente os campos em '~' e, em seguida, percorrendo todos os campos disponíveis por linha. Somente quando um campo é necessário, ele é dividido em subcampos em '*' para obter os elementos solicitados. 'get_slice' retorna "" se nada for encontrado, então isso deve ser verificado.
Acho que entendi a pergunta..