Dosyayı bölün ve ilgili veri yapısına koyun?


10

Aşağıdaki tek satır içeren bir dosya var:

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

içinde iki veri setim var:

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

Şimdi, yukarıdaki dosyayı okumam ve her makinenin bilgilerini yukarıda belirtildiği gibi ayıklayabilmem ve bazı veri yapısında saklayabilmem için bölmem gerekiyor.

Şu anda, bash kabuk betiğinde hangi veri yapısını kullanmam gerektiği konusunda kafam karıştı. Bunu Java'da yapsaydım, kullanardım Map<String, Set<String>>ama bir kabuk betiğinde ne kullanmam gerektiğinden emin değilim.

Ve bazı veri yapılarında sakladıktan sonra, onu tekrarlamam ve sonucu yazdırmam gerekiyor.

Aşağıdaki kabuk betiğini kullanarak yukarıdaki dosyayı okuyabiliyorum:

#!/bin/bash

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

Ancak yukarıdaki satır verilerini nasıl ayıracağımdan emin değilim, böylece her makine bilgisini çıkarabilir ve daha sonra bazı veri yapısında depolayabilir miyim?

GÜNCELLEME:-

Aşağıda benim glenn tarafından verilen öneri takip ettikten sonra kabuk komut dosyası var -

#!/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

Yanıtlar:


4

bash veri yapıları 2 boyutlu diziler için bile çok temeldir. Kullanmak istediğiniz aracın bu olduğundan emin misiniz?

Bash ve GNU grep ile bu tatsız karmaşayı yapabilirsiniz:

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

Bu oldukça kırılgan. Perl'i böyle bir şey için kullanırdım: hala çirkin ama daha özlü

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
                        ]
        };

Öneri için teşekkürler. Sonunda scp kullanmam gerektiğinden kabuk komut dosyası seçeneğiyle gidebilirim, böylece kabuk komut dosyasında scp yapmak kolay olacak. Ama neyse, bunun nasıl gittiğini görelim. Sorunuzu, önerinizi ekledikten sonra kullanıyor olabileceğim gerçek kabuk komut dosyası ile güncelledim. Lütfen bir göz atın ve doğru göründüğünü bana bildirin ve değiştirmek istediğiniz bir şey varsa bana da bildirin.
SSH

+1 evalOrada ile oldukça kaygan bir hareket .
Joseph R.

1

Kabuk metin işleme yardımcı programları, öncelikle boşluk veya sabit karakterle ayrılmış satır ve alanlar başına bir kayıtla temsil edilen verileri işlemek üzere tasarlanmıştır. Bu biçim tamamen farklıdır ve basit bir şekilde işleyemezsiniz.

Bir yaklaşım, dosyayı kolayca işlenebilecek biçime uyacak şekilde önceden işlemektir. Parantezlerin ve parantezlerin burada tasvir edilenlerden başka bir şekilde kullanılmadığını varsayıyorum (tüm metnin etrafındaki parantezler, makine değer listelerinin etrafındaki parantezler).

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

Sonuç satır başına bir makine ve kayıtları ayırmak için virgül içerir. Aşağıdaki snippet, her satırdaki makine adını ayrıştırır ve içinde virgülle ayrılmış değerler listesi bırakır values.

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

Aşağıdaki bash'a özgü snippet, değerleri bir diziye koyar.

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

@Giles: Öneri için teşekkürler. Her makine için toplam dosya sayısını elde etmek de mümkün müdür? yukarıdaki yukarıdaki komutu kullanarak toplam sayım anlamına gelir? Yukarıdaki örnekte olduğu gibi, machineA'nın dört dosyası ve machineB'nin de dört dosyası vardır
SSH

@SSH Düzenlememe bakın.
Gilles 'SO- kötü olmayı kes

0

awkGörevi tamamlamak için kullanabilirsiniz .

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]

Teşekkürler John. Her makine için toplam dosya sayısını elde etmek mümkün mü? Yukarıdaki örnekte olduğu gibi, machineA'nın dört dosyası ve machineB'nin de dört dosyası vardır. Bunu elde etmek de mümkün mü?
SSH

0

Bu biraz JSON'a benziyor. Doğru JSON olarak düzeltebilir ve JSON araçlarını kullanabilirsiniz:

$ 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"
   ]
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.