¿Dividir el archivo y ponerlo en la estructura de datos correspondiente?

10

Tengo un archivo que contiene la única línea a continuación:

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

en el que tengo dos conjuntos de datos:

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

Ahora, necesito leer el archivo anterior y dividirlo de tal manera que pueda extraer la información de cada máquina como se mencionó anteriormente y almacenarla en alguna estructura de datos.

Actualmente, estoy confundido sobre qué estructura de datos debería usar en un script de shell bash. Si estuviera haciendo esto en Java, lo usaría Map<String, Set<String>>pero no estoy seguro de qué debo usar en un script de shell.

Y después de almacenarlo en alguna estructura de datos, necesito iterarlo e imprimir el resultado.

Puedo leer el archivo anterior usando el script de shell a continuación:

#!/bin/bash

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

Pero no estoy seguro de cómo dividir los datos de la línea anterior de tal manera que pueda extraer la información de cada máquina y luego almacenarla en alguna estructura de datos.

ACTUALIZAR:-

A continuación se muestra mi script de shell que tengo después de seguir la sugerencia 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
fuente

Respuestas:

4

Las estructuras de datos bash son demasiado rudimentarias incluso para matrices bidimensionales. ¿Estás seguro de que esta es la herramienta que quieres usar?

Con bash y GNU grep, puedes hacer este desastre desagradable:

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

Esto es bastante frágil. Usaría Perl para algo como esto: todavía feo pero más 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
fuente
Gracias por la sugerencia Podría ir con la opción de script de shell, ya que finalmente necesito usar scp, así que creo que hacer scp en script de shell será fácil. Pero de todos modos, veamos cómo sale esto. He actualizado mi pregunta con el script de shell real que podría estar usando después de incorporar su sugerencia. Eche un vistazo y avíseme si parece correcto y si hay algo que le gustaría modificar, hágamelo saber también.
SSH
+1 Movimiento bastante hábil con el eval, allí.
Joseph R.
1

Las utilidades de procesamiento de texto de shell están diseñadas principalmente para manipular datos representados con un registro por línea y campos separados por espacios en blanco o un carácter fijo. Este formato es completamente diferente y no podrá procesarlo de manera directa.

Un enfoque es preprocesar el archivo para que se ajuste al tipo de formato que se puede procesar fácilmente. Supongo que los corchetes y los corchetes no se usan de ninguna otra manera que no se muestra aquí (corchetes alrededor del texto completo, corchetes alrededor de las listas de valores de la máquina).

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

El resultado tiene una máquina por línea y comas para separar los registros. El siguiente fragmento analiza el nombre de la máquina en cada línea y deja una lista de valores separados por comas values.

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

El siguiente fragmento específico de bash coloca los valores en una matriz.

 | while IFS=, read -r -a values; do
  machine=${values[0]}; shift values
  echo "There are ${#values[@]} on machine $machine"
done
Gilles 'SO- deja de ser malvado'
fuente
@Giles: Gracias por la sugerencia. ¿También es posible obtener el número total de archivos para cada máquina? significa el recuento total usando el mismo comando anterior? Como, por ejemplo, la máquina A tiene cuatro archivos y la máquina B también tiene cuatro archivos
SSH
@SSH Ver mi edición.
Gilles 'SO- deja de ser malvado'
0

Puedes usar awkpara completar la tarea.

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
fuente
Gracias John ¿Es posible obtener el número total de archivos también para cada máquina? Como, por ejemplo, la máquina A tiene cuatro archivos y la máquina B también tiene cuatro archivos. ¿Es posible obtener eso también?
SSH
0

Esto se parece un poco a JSON. Puede arreglarlo para que sea JSON adecuado y usar herramientas 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.
fuente