Розділити файл і помістити його у відповідну структуру даних?


10

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

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

у якому я маю два набори даних:

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

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

Наразі я плутаю, яку структуру даних я повинен використовувати в скрипті bash shell. Якби я робив це на 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

Відповіді:


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

@Giles: Дякую за пропозицію. Чи можливо також отримати загальну кількість файлів для кожної машини? означає загальний підрахунок, використовуючи ту саму вищевказану команду? Як, наприклад, у прикладі machineA є чотири файли, а machineB також чотири файли
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]

Дякую, Джон. Чи можливо отримати загальну кількість файлів також для кожної машини. Як, наприклад, у прикладі machineA є чотири файли, а machineB також чотири файли. Чи можливо це також отримати?
СШ

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"
   ]
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.