Podziel plik i umieść go w odpowiedniej strukturze danych?

10

Mam plik zawierający jedną linię poniżej:

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

w którym mam dwa zestawy danych:

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

Teraz muszę przeczytać powyższy plik i podzielić go w taki sposób, aby wyodrębnić informacje o każdym komputerze, jak wspomniano powyżej, i zapisać je w jakiejś strukturze danych.

Obecnie nie jestem pewien, jakiej struktury danych powinienem używać w skrypcie powłoki bash. Gdybym robił to w Javie, skorzystałbym, Map<String, Set<String>>ale nie jestem pewien, czego powinienem użyć w skrypcie powłoki.

Po zapisaniu go w jakiejś strukturze danych muszę go powtórzyć i wydrukować wynik.

Jestem w stanie odczytać powyższy plik za pomocą poniższego skryptu powłoki:

#!/bin/bash

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

Ale nie jestem pewien, jak podzielić powyższe dane wiersza w taki sposób, aby móc wyodrębnić informacje o każdej maszynie, a następnie zapisać je w jakiejś strukturze danych?

AKTUALIZACJA:-

Poniżej znajduje się mój skrypt powłoki, który otrzymałem po wykonaniu sugestii 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
źródło

Odpowiedzi:

4

Struktury danych bash są zbyt podstawowe dla tablic nawet dwuwymiarowych. Czy jesteś pewien, że to narzędzie, którego chcesz użyć?

Dzięki bash i GNU grep możesz zrobić ten niesmaczny bałagan:

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

To jest dość kruche. Użyłbym Perla do czegoś takiego: wciąż brzydkiego, ale bardziej zwięzłego

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
źródło
Dziękuję za sugestie. Mogę przejść z opcją skryptu powłoki, ponieważ w końcu muszę użyć scp, więc uważam, że wykonanie scp w skrypcie powłoki będzie łatwe. Ale tak czy inaczej, zobaczmy, jak to się potoczy. Zaktualizowałem moje pytanie o rzeczywisty skrypt powłoki, którego mogę używać po uwzględnieniu Twojej sugestii. Proszę spojrzeć i poinformować mnie, czy wygląda poprawnie, a jeśli jest coś, co chcielibyście zmodyfikować, proszę również o informację.
SSH,
+1 Zręczny ruch z eval, tam.
Joseph R.
1

Narzędzia do przetwarzania tekstu powłoki są zaprojektowane przede wszystkim do manipulowania danymi reprezentowanymi przez jeden rekord na linię i pola oddzielone białymi znakami lub stałym znakiem. Ten format jest zupełnie inny i nie będzie można go przetworzyć w prosty sposób.

Jednym z podejść jest wstępne przetworzenie pliku w celu dopasowania do formatu, który można łatwo przetworzyć. Zakładam, że nawiasy klamrowe i nawiasy klamrowe nie są używane w żaden inny sposób niż tutaj przedstawiony (nawiasy klamrowe wokół całego tekstu, nawiasy klamrowe wokół list wartości maszynowych).

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

Wynik ma jedną maszynę na linię i przecinki do oddzielania rekordów. Poniższy fragment kodu analizuje nazwę komputera w każdym wierszu i pozostawia listę wartości oddzielonych przecinkami values.

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

Poniższy fragment kodu bash umieszcza wartości w tablicy.

 | while IFS=, read -r -a values; do
  machine=${values[0]}; shift values
  echo "There are ${#values[@]} on machine $machine"
done
Gilles „SO- przestań być zły”
źródło
@Giles: Dzięki za sugestię. Czy można również uzyskać łączną liczbę plików dla każdego komputera? co oznacza łączną liczbę przy użyciu tego samego powyższego polecenia? Jak na przykład powyżej, maszyna A ma cztery pliki, a maszyna B także cztery pliki
SSH,
@SSH Zobacz moją edycję.
Gilles „SO - przestań być zły”,
0

Możesz użyć awkdo wykonania zadania.

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.
źródło
Dzięki, John. Czy można również uzyskać całkowitą liczbę plików dla każdego komputera. Jak na przykład powyżej, maszyna A ma cztery pliki, a maszyna B także cztery pliki. Czy to również możliwe?
SSH,
0

To wygląda trochę jak JSON. Możesz to naprawić, aby był prawidłowy JSON i używać narzędzi 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.
źródło