Como posso classificar blocos de dados de comprimento variável em um campo em cada bloco

2

Eu tenho um arquivo RDF com blocos de dados de vários números de linhas delineados por < e /> . Dentro de cada bloco, existe um campo identificado por name="some name" . Eu preciso classificar os blocos no valor de name sem alterar a ordem de qualquer uma das linhas dentro de cada bloco. Além disso, existe um campo em cada bloco com um número. Eu preciso renumerar esses campos de 1 para n com base na posição classificada de cada bloco.

Aqui está um exemplo de 3 blocos:

<RDF:Description RDF:about="rdf:#$CHROME1"
 NS1:name="AAA Carolinas"
  NS1:urlToUse=""
  NS1:whereLeetLB="off"
  NS1:leetLevelLB="1"
  NS1:hashAlgorithmLB="md5"
  NS1:passwordLength="16"
  NS1:usernameTB="user"
  NS1:counter=""
  NS1:charset="a9b0c8d1e7f2g6h3i5j4klmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV123456789"
  NS1:prefix="6%Fl"
  NS1:suffix="I$5g"
  NS1:protocolCB="false"
  NS1:subdomainCB="true"
  NS1:domainCB="true"
  NS1:pathCB="false"
  />
<RDF:Description RDF:about="rdf:#$CHROME2"
 NS1:name="Adobe Forums"
  NS1:urlToUse="adobeforums.com"
  NS1:whereLeetLB="off"
  NS1:leetLevelLB="1"
  NS1:hashAlgorithmLB="md5"
  NS1:passwordLength="12"
  NS1:usernameTB="username"
  NS1:counter=""
  NS1:charset="a9b0c8d1e7f2g6h3i5j4klmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV"
  NS1:prefix=""
  NS1:suffix=""
  NS1:protocolCB="false"
  NS1:subdomainCB="true"
  NS1:domainCB="true"
  NS1:pathCB="false"
  NS1:pattern0="*adobeforums.com*"
  NS1:patternenabled0="true"
  NS1:patterndesc0=""
  NS1:patterntype0="wildcard"
  />
<RDF:Description RDF:about="rdf:#$CHROME3"
 NS1:name="Adorama"
  NS1:urlToUse="adorama.com"
  NS1:whereLeetLB="off"
  NS1:leetLevelLB="1"
  NS1:hashAlgorithmLB="md5"
  NS1:passwordLength="8"
  NS1:usernameTB="username"
  NS1:counter=""
  NS1:charset="a9b0c8d1e7f2g6h3i5j4klmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV"
  NS1:prefix=""
  NS1:suffix=""
  NS1:protocolCB="false"
  NS1:subdomainCB="false"
  NS1:domainCB="true"
  NS1:pathCB="false"
  NS1:pattern0="*adorama.com*"
  NS1:patternenabled0="true"
  NS1:patterndesc0=""
  NS1:patterntype0="wildcard"
  NS1:pattern1="www.adoramapix.com*"
  NS1:patternenabled1="true"
  NS1:patterndesc1=""
  NS1:patterntype1="wildcard"
  />

O número ao qual aludi é o número que segue $CHROME no exemplo acima. Eu sou um antigo programador Assembler, COBOL, Fortran, Basic, mas não estou pronto para criar scripts ou linguagens mais novas. Eu provavelmente poderia fazer isso em um programa básico, mas eu gostaria de uma solução Linux, se possível.

    
por Cecil 17.07.2018 / 19:36

1 resposta

0

Espero que haja algum caractere - ou pelo menos alguma string - que nunca aparece no seu arquivo. Eu assumirei que isso é verdade para | . Para ser mais seguro, usarei || .

Execute este comando:

sed -n -e H -e '/^ *\/> *$/ { s/.*//; x; s/.*NS1:name="\([^"]*\)/&/; s/\n/||/gp }' your_file \
        | sort \
        | nl -ba \
        | sed -e 's/ *\([0-9]*\)[^|]*||\(.* RDF:about="rdf:#$CHROME\)[0-9]*//' -e 's/||/\n/g'

Nota: Isso (provavelmente) requer que você tenha o GNU sed.

Visão geral

  • Use sed para transformar o arquivo em um formato adequado para classificação (detalhes abaixo).
  • Classifique a saída de sed .
  • Aplicar números de linha (pré-anexados). Use qualquer comando que gere números adequados. Eu gosto de nl -ba , mas cat -n funcionaria tão bem, e provavelmente há outras opções.
  • Use sed para remover o número da linha desde o começo da linha e insira-o após CHROME . Desprenda os dados de volta para o formato original.

Detalhes - Primeiro sed de comando

O comando sort trata cada linha como um registro. Portanto, pegamos cada registro (delimitado) do seu arquivo de entrada e concatenar todas as linhas, formando uma linha longa. Também copiamos o valor name para o início da linha, para evitar ter que especificar uma chave de classificação.

  • Use a opção -n para suprimir a impressão automática. As linhas serão impressas apenas quando dissermos p .
  • Execute H em todas as linhas. Isso acrescenta a linha atual ao espaço de espera. Logicamente, isso pode fazer mais sentido para copiar a linha < para o espaço de espera (com o comando h ) e, em seguida, anexe todas as linhas subsequentes. Eu arbitrariamente escolhi essa abordagem.

    Observe que, como adicionamos a linha < a um espaço vazio, o registro agregado tem uma nova linha extra no começo.

  • Procure uma linha contendo /> , opcionalmente precedido e / ou seguido por espaços. Quando a encontramos, sabemos que temos um registro completo no espaço de espera. Faça os seguintes comandos apenas nessas linhas.
    • s/.*// limpa o espaço do padrão (isto é, apaga a linha /> ). Isso não está realmente jogando fora qualquer informação; a linha /> já foi anexada ao espaço de espera (porque toda a linha é anexada ao espaço de espera).
    • x troca o espaço de padrões e o espaço de armazenamento. Isso recupera o registro agregado (anexado / concatenado) do espaço de espera no espaço padrão. Por causa do comando anterior ( s/.*// ), isso limpa o espaço de espera.
    • s/.*NS1:name="\([^"]*\)/&/ procura o campo de nome e copia seu valor para o início do registro. Isso falhará se você puder ter um nome com caracteres de citação nele.
    • s/\n/||/gp substitui cada nova linha no espaço padrão com || . (Esta é a etapa que converte o registro em uma linha). Por causa do p , isso imprime o registro.

A saída do primeiro comando sed , quando executada em seu arquivo de amostra, é

AAA Carolinas||<RDF:Description RDF:about="rdf:#$CHROME1"|| NS1:name="AAA Carolinas"||  NS1:urlToUse=""||  NS1:whereLeetLB="off"||  NS1:leetLevelLB="1"||  NS1:hashAlgorithmLB="md5"||  NS1:passwordLength="16"||  NS1:usernameTB="user"||  NS1:counter=""||  NS1:charset="a9b0c8d1e7f2g6h3i5j4klmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV123456789"||  NS1:prefix="6%Fl"||  NS1:suffix="I$5g"||  NS1:protocolCB="false"||  NS1:subdomainCB="true"||  NS1:domainCB="true"||  NS1:pathCB="false"||  />
Adobe Forums||<RDF:Description RDF:about="rdf:#$CHROME2"|| NS1:name="Adobe Forums"||  NS1:urlToUse="adobeforums.com"||  NS1:whereLeetLB="off"||  NS1:leetLevelLB="1"||  NS1:hashAlgorithmLB="md5"||  NS1:passwordLength="12"||  NS1:usernameTB="username"||  NS1:counter=""||  NS1:charset="a9b0c8d1e7f2g6h3i5j4klmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV"||  NS1:prefix=""||  NS1:suffix=""||  NS1:protocolCB="false"||  NS1:subdomainCB="true"||  NS1:domainCB="true"||  NS1:pathCB="false"||  NS1:pattern0="*adobeforums.com*"||  NS1:patternenabled0="true"||  NS1:patterndesc0=""||  NS1:patterntype0="wildcard"||  />
Adorama||<RDF:Description RDF:about="rdf:#$CHROME3"|| NS1:name="Adorama"||  NS1:urlToUse="adorama.com"||  NS1:whereLeetLB="off"||  NS1:leetLevelLB="1"||  NS1:hashAlgorithmLB="md5"||  NS1:passwordLength="8"||  NS1:usernameTB="username"||  NS1:counter=""||  NS1:charset="a9b0c8d1e7f2g6h3i5j4klmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV"||  NS1:prefix=""||  NS1:suffix=""||  NS1:protocolCB="false"||  NS1:subdomainCB="false"||  NS1:domainCB="true"||  NS1:pathCB="false"||  NS1:pattern0="*adorama.com*"||  NS1:patternenabled0="true"||  NS1:patterndesc0=""||  NS1:patterntype0="wildcard"||  NS1:pattern1="www.adoramapix.com*"||  NS1:patternenabled1="true"||  NS1:patterndesc1=""||  NS1:patterntype1="wildcard"||  />

Detalhes - Segundo sed comando

  • s/ *\([0-9]*\)[^|]*||\(.* RDF:about="rdf:#$CHROME\)[0-9]*// divide a linha em pedaços:

    • Zero ou mais espaços.
    • O número da linha (zero ou mais dígitos). Isso se torna o grupo .
    • A guia após o número da linha, o valor name e o || após ele.
    • O registro foi feito em RDF:about="rdf:#$CHROME . Isso se torna o grupo .
    • O número do registro antigo (zero ou mais dígitos).
    • Implicitamente, o resto do registro.

    Em seguida, substitui as cinco primeiras peças com RDF:about="rdf:#$CHROME e o número da linha (o novo número de registro). Como o resto do registro não foi correspondido, não é afetado pelo comando.

  • s/||/\n/g substitui cada || por uma nova linha, restaurar (recriar) a estrutura original de várias linhas do arquivo.

Obviamente,…

... para enviar a saída para um arquivo, adicione > your_output_file no final da última linha do comando (ou seja, no final do segundo sed ). Você pode então mover ( mv ) your_output_file para o seu arquivo original. Não faz sentido algum especificar a opção --output= (ou -o ) para o comando sort ; a saída de sort deve entrar no comando que aplica os números de linha. Se você quiser capturar um arquivo intermediário, diga isso.

    
por 24.07.2018 / 08:37