Dividir o arquivo e colocá-lo na estrutura de dados correspondente?

10

Eu tenho um arquivo que contém a única linha abaixo:

{machineA=[0, 1024, 4, 1028], machineB=[1, 1025, 5, 1029]}

em que tenho dois conjuntos de dados:

machineA=[0, 1024, 4, 1028]
machineB=[1, 1025, 5, 1029]

Agora, preciso ler o arquivo acima e dividi-lo de forma que possa extrair as informações de cada máquina conforme mencionado acima e armazená-las em alguma estrutura de dados.

Atualmente, estou confuso sobre qual estrutura de dados devo usar em um script de shell bash. Se eu estivesse fazendo isso em Java, usaria, Map<String, Set<String>>mas não tenho certeza do que devo usar em um shell script.

E depois de armazená-lo em alguma estrutura de dados, preciso iterá-lo e imprimir o resultado.

Consigo ler o arquivo acima usando o script de shell abaixo:

#!/bin/bash

while read -r line; do
       echo "$line"
    done < data.txt

Mas não sei como dividir os dados da linha acima de maneira a poder extrair as informações de cada máquina e armazená-las em alguma estrutura de dados?

ATUALIZAR:-

Abaixo está o meu script de shell que recebi depois de seguir a sugestão dada por glenn -

#!/bin/bash

while read -r line; do
       echo "$line"
       declare -A "$(
            echo "x=("
            grep -oP '(\w+)(?==)|(?<==\[).*?(?=\])' <<< "$line" |
              tr -d , |
              paste - - |
              sed 's/^/[/; s/\t/]="/; s/$/"/'
            echo ")"
        )"

        for key in "${!x[@]}"; do           # need quotes here
            for element in ${x[$key]}; do   # no quotes here
                printf "%s\t%s\n" "$key" "$element"
            done
        done    
    done < primary.txt
SSH
fonte

Respostas:

4

estruturas de dados do bash são muito rudimentares para matrizes bidimensionais. Tem certeza de que esta é a ferramenta que deseja usar?

Com o bash e o GNU grep, você pode fazer essa bagunça desagradável:

line='{machineA=[0, 1024, 4, 1028], machineB=[1, 1025, 5, 1029]}'
declare -A "$(
    echo "x=("
    grep -oP '(\w+)(?==)|(?<==\[).*?(?=\])' <<< "$line" |
      tr -d , |
      paste - - |
      sed 's/^/[/; s/\t/]="/; s/$/"/'
    echo ")"
)"

for key in "${!x[@]}"; do           # need quotes here
    for element in ${x[$key]}; do   # no quotes here
        printf "%s\t%s\n" "$key" "$element"
    done
done
machineA    0
machineA    1024
machineA    4
machineA    1028
machineB    1
machineB    1025
machineB    5
machineB    1029

Isso é bem frágil. Eu usaria o Perl para algo assim: ainda feio, mas mais conciso

echo "$line" | perl -MData::Dumper -ne '
    s/=\[/=>[/g; 
    eval "\$x=$_";
    # do something with your data structure (a hash of arrays) 
    print Dumper($x)
'
$VAR1 = {
          'machineB' => [
                          1,
                          1025,
                          5,
                          1029
                        ],
          'machineA' => [
                          0,
                          1024,
                          4,
                          1028
                        ]
        };
Glenn Jackman
fonte
Obrigado pela sugestão. Eu posso usar a opção de script de shell, pois finalmente preciso usar o scp, então acredito que será fácil fazer scp no script de shell. De qualquer forma, vamos ver como isso acontece. Atualizei minha pergunta com o script de shell real que eu poderia estar usando depois de incorporar sua sugestão. Dê uma olhada e deixe-me saber se parece correto e se há algo que você gostaria de modificar, me avise também.
SSH
+1 Movimento bem liso com o eval, lá.
Joseph R.
1

Os utilitários de processamento de texto do shell são projetados principalmente para manipular dados representados com um registro por linha e campos separados por espaço em branco ou por um caractere fixo. Esse formato é completamente diferente e você não poderá processá-lo de maneira direta.

Uma abordagem é pré-processar o arquivo para se ajustar ao tipo de formato que pode ser processado facilmente. Suponho que colchetes e chaves não sejam usados ​​de maneira alguma que não sejam os retratados aqui (chaves em todo o texto, colchetes em torno das listas de valores da máquina).

<data.txt sed -e 's/^{//' -e 's/}$//' -e 's/ *= *\[/,/g' -e 's/, */,/g' -e 's/\] *$//' -e 's/] *, */\n/g'

O resultado possui uma máquina por linha e vírgulas para separar os registros. O seguinte trecho analisa o nome da máquina em cada linha e deixa uma lista de valores separados por vírgula values.

 | while IFS=, read -r machine values; do 

O seguinte snippet específico do bash coloca os valores em uma matriz.

 | while IFS=, read -r -a values; do
  machine=${values[0]}; shift values
  echo "There are ${#values[@]} on machine $machine"
done
Gilles 'SO- parar de ser mau'
fonte
@ Giles: Obrigado pela sugestão. Também é possível obter o número total de arquivos para cada máquina? significando a contagem total usando o mesmo comando acima? Como, por exemplo, acima, machineA possui quatro arquivos e machineB também possui quatro arquivos
SSH
@SSH Veja minha edição.
Gilles 'SO- stop be evil'
0

Você pode usar awkpara concluir a tarefa.

awk -F "], " '/[a-zA-Z]=\[[0-9]/ {gsub(/{|}/,""); for(i=1; i<=NF; i++) if($i !~ /\]$/) print $i"]"; else print $i}' data.txt

machineA=[0, 1024, 4, 1028]
machineB=[1, 1025, 5, 1029]
John B
fonte
Obrigado John. É possível obter o número total de arquivos também para cada máquina. Como no exemplo acima, a máquina A possui quatro arquivos e a máquina B também possui quatro arquivos. É possível conseguir isso também?
SSH
0

Parece um pouco com JSON. Você pode corrigi-lo como JSON adequado e usar as ferramentas JSON:

$ echo '{machineA=[0, 1024, 4, 1028], machineB=[1, 1025, 5, 1029]}' |  perl -pe 's!\b!"!g; s/=/:/g' | json_pp
{
   "machineB" : [
      "1",
      "1025",
      "5",
      "1029"
   ],
   "machineA" : [
      "0",
      "1024",
      "4",
      "1028"
   ]
}
Vi.
fonte