Разбить файл и поместить его в соответствующую структуру данных?

10

У меня есть файл, который содержит одну строку ниже:

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

в котором у меня есть два набора данных:

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

Теперь мне нужно прочитать вышеуказанный файл и разбить его таким образом, чтобы я мог извлечь информацию о каждой машине, как указано выше, и сохранить ее в некоторой структуре данных.

В настоящее время я не понимаю, какую структуру данных мне следует использовать в сценарии оболочки bash. Если бы я делал это на Java, я бы использовал, Map<String, Set<String>>но я не уверен, что я должен использовать в сценарии оболочки.

И после сохранения в некоторой структуре данных, мне нужно повторить и распечатать результат.

Я могу прочитать вышеуказанный файл, используя скрипт ниже:

#!/bin/bash

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

Но я не уверен, как разделить вышеуказанные строчные данные таким образом, чтобы я мог извлечь информацию о каждой машине и затем сохранить ее в некоторой структуре данных?

ОБНОВИТЬ:-

Ниже приведен мой сценарий оболочки, который я получил после того, как следовал предложению 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
источник

Ответы:

4

Структуры данных bash слишком элементарны даже для двумерных массивов. Вы уверены, что это инструмент, который вы хотите использовать?

С помощью bash и GNU grep вы можете сделать этот неприятный беспорядок:

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

Это довольно хрупко. Я бы использовал Perl для чего-то вроде этого: все еще некрасиво, но более кратко

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
                        ]
        };
Гленн Джекман
источник
Спасибо за предложение. Я мог бы пойти с опцией сценария оболочки, так как, наконец, мне нужно использовать scp, поэтому я считаю, что сделать scp в сценарии оболочки будет легко. Но в любом случае, давайте посмотрим, как это выходит. Я обновил свой вопрос фактическим сценарием оболочки, который мог бы использовать после включения вашего предложения. Пожалуйста, посмотрите и дайте мне знать, если это выглядит правильно, и если вы хотите что-то изменить, дайте мне знать.
SSH
+1 Довольно скользко eval, там.
Джозеф Р.
1

Утилиты обработки текста оболочки в первую очередь предназначены для манипулирования данными, представленными одной записью на строку и полями, разделенными пробелами или фиксированным символом. Этот формат совершенно другой, и вы не сможете обработать его простым способом.

Один из подходов заключается в предварительной обработке файла для соответствия типу формата, который может быть легко обработан. Я предполагаю, что скобки и фигурные скобки не используются ни в каком другом виде, кроме как здесь изображены (фигурные скобки вокруг всего текста, квадратные скобки вокруг списков машинных значений).

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

Результат имеет один компьютер на строку и запятые для разделения записей. Следующий фрагмент разбирает имя машины в каждой строке и оставляет список значений через запятую values.

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

Следующий специфичный для bash фрагмент кода помещает значения в массив.

 | while IFS=, read -r -a values; do
  machine=${values[0]}; shift values
  echo "There are ${#values[@]} on machine $machine"
done
Жиль "ТАК - перестань быть злым"
источник
@ Джайлс: Спасибо за предложение. Можно ли также получить общее количество файлов для каждой машины? имеется в виду общее количество с использованием той же команды выше? Как и в приведенном выше примере, машина A имеет четыре файла, а машина B также имеет четыре файла
SSH
@SSH Смотрите мои изменения.
Жиль "ТАК - перестань быть злым"
0

Вы можете использовать awkдля выполнения задачи.

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]
Джон Б
источник
Спасибо Джон. Можно ли получить общее количество файлов также для каждой машины. Как и в приведенном выше примере, машина A имеет четыре файла, а машина B также имеет четыре файла. Возможно ли это получить?
SSH
0

Это немного похоже на JSON. Вы можете исправить это, чтобы быть правильным JSON и использовать инструменты 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.
источник