Dividere il file e inserirlo nella struttura dati corrispondente?


10

Ho un file che contiene la riga singola di seguito:

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

in cui ho due set di dati:

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

Ora, ho bisogno di leggere il file sopra e dividerlo in modo tale da poter estrarre le informazioni di ogni macchina come menzionato sopra e memorizzarle in una struttura di dati.

Attualmente, sono confuso quale struttura di dati dovrei usare in uno script di shell bash. Se lo facessi in Java, lo userei Map<String, Set<String>>ma non sono sicuro di cosa dovrei usare in uno script di shell.

E dopo averlo archiviato in una struttura di dati, devo iterarlo e stampare il risultato.

Sono in grado di leggere il file sopra usando lo script di shell qui sotto:

#!/bin/bash

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

Ma non sono sicuro di come dividere i dati della riga sopra in modo tale da poter estrarre le informazioni di ogni macchina e quindi memorizzarle in una struttura di dati?

AGGIORNARE:-

Di seguito è riportato il mio script shell che ho ricevuto dopo aver seguito il suggerimento dato da 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

Risposte:


4

le strutture di dati bash sono troppo rudimentali per matrici anche bidimensionali. Sei sicuro che questo sia lo strumento che vuoi usare?

Con bash e GNU grep, puoi fare questo disgustoso pasticcio:

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

Questo è piuttosto fragile. Userei Perl per qualcosa del genere: ancora brutto ma più 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
                        ]
        };

Grazie per il suggerimento Potrei scegliere l'opzione shell script poiché alla fine ho bisogno di usare scp, quindi credo che fare scp in shell script sia facile. Comunque, vediamo come va. Ho aggiornato la mia domanda con l'attuale script di shell che potrei usare dopo aver incorporato il tuo suggerimento. Per favore, dai un'occhiata e fammi sapere se sembra corretto e se ci sono qualcosa che vorresti modificare, fammelo sapere pure.
SSH,

+1 Mossa abbastanza fluida con eval, lì.
Joseph R.

1

Le utility di elaborazione del testo della shell sono progettate principalmente per manipolare i dati rappresentati con un record per riga e campi separati da uno spazio bianco o da un carattere fisso. Questo formato è completamente diverso e non sarai in grado di elaborarlo in modo semplice.

Un approccio consiste nel preelaborare il file per adattarlo al tipo di formato che può essere elaborato facilmente. Suppongo che parentesi e parentesi graffe non vengano utilizzate in alcun modo diverso da quello rappresentato qui (parentesi graffe intorno al testo intero, parentesi attorno agli elenchi di valori della macchina).

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

Il risultato ha una macchina per riga e virgole per separare i record. Il frammento seguente analizza il nome della macchina su ogni riga e lascia un elenco di valori separati da virgola values.

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

Il seguente frammento specifico di bash inserisce i valori in un array.

 | while IFS=, read -r -a values; do
  machine=${values[0]}; shift values
  echo "There are ${#values[@]} on machine $machine"
done

@Giles: grazie per il suggerimento. È anche possibile ottenere il numero totale di file per ogni macchina? intendi il conteggio totale usando lo stesso comando sopra? Come per esempio sopra, machineA ha quattro file e machineB ha anche quattro file
SSH il

@SSH Vedi la mia modifica.
Gilles 'SO- smetti di essere malvagio' il

0

È possibile utilizzare awkper completare l'attività.

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]

Grazie John È possibile ottenere anche il numero totale di file per ogni macchina. Come per esempio sopra, machineA ha quattro file e machineB ha anche quattro file. È possibile ottenere anche quello?
SSH,

0

Sembra un po 'JSON. È possibile correggerlo per essere JSON corretto e utilizzare gli strumenti 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"
   ]
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.