Tách tập tin và đặt nó trong cấu trúc dữ liệu tương ứng?


10

Tôi có một tập tin chứa dòng duy nhất dưới đây:

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

trong đó tôi có hai bộ dữ liệu:

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

Bây giờ, tôi cần đọc tệp trên và chia nó theo cách mà tôi có thể trích xuất thông tin của từng máy như đã đề cập ở trên và lưu trữ nó trong một số cấu trúc dữ liệu.

Hiện tại, tôi bối rối không biết nên sử dụng cấu trúc dữ liệu nào trong tập lệnh bash shell. Nếu tôi đang làm điều này trong Java, tôi sẽ sử dụng Map<String, Set<String>>nhưng tôi không chắc mình nên sử dụng cái gì trong tập lệnh shell.

Và sau khi lưu trữ nó trong một số cấu trúc dữ liệu, tôi cần lặp lại nó và in ra kết quả.

Tôi có thể đọc tệp trên bằng cách sử dụng tập lệnh shell bên dưới:

#!/bin/bash

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

Nhưng tôi không chắc làm thế nào để phân chia dữ liệu dòng trên theo cách sao cho tôi có thể trích xuất từng máy thông tin và sau đó lưu trữ nó trong một số cấu trúc dữ liệu?

CẬP NHẬT: -

Dưới đây là kịch bản shell của tôi, tôi đã nhận được sau khi làm theo gợi ý được đưa ra bởi 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

Câu trả lời:


4

cấu trúc dữ liệu bash quá thô sơ đối với các mảng 2 chiều. Bạn có chắc chắn đây là công cụ bạn muốn sử dụng?

Với bash và GNU grep, bạn có thể thực hiện mớ hỗn độn khó chịu này:

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

Điều này là khá mong manh. Tôi sẽ sử dụng Perl cho một cái gì đó như thế này: vẫn xấu xí nhưng súc tích hơn

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

Cảm ơn đã gợi ý. Tôi có thể đi với tùy chọn shell script vì cuối cùng tôi cần sử dụng scp vì vậy tôi tin rằng việc thực hiện scp trong shell script sẽ dễ dàng. Nhưng dù sao, hãy xem làm thế nào điều này đi ra ngoài. Tôi đã cập nhật câu hỏi của mình với tập lệnh shell thực tế mà tôi có thể đang sử dụng sau khi kết hợp đề xuất của bạn. Xin hãy xem và cho tôi biết nếu nó có vẻ chính xác và nếu có bất cứ điều gì bạn muốn sửa đổi thì hãy cho tôi biết.
SSH

+1 Di chuyển khá trơn tru với eval, ở đó.
Joseph R.

1

Các tiện ích xử lý văn bản shell được thiết kế chủ yếu để thao tác dữ liệu được biểu thị bằng một bản ghi trên mỗi dòng và các trường được phân tách bằng khoảng trắng hoặc ký tự cố định. Định dạng này là hoàn toàn khác nhau và bạn sẽ không thể xử lý nó theo cách đơn giản.

Một cách tiếp cận là tiền xử lý tệp để phù hợp với loại định dạng có thể được xử lý dễ dàng. Tôi giả sử rằng dấu ngoặc và dấu ngoặc không được sử dụng theo bất kỳ cách nào khác ngoài được miêu tả ở đây (dấu ngoặc quanh toàn bộ văn bản, dấu ngoặc quanh danh sách giá trị máy).

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

Kết quả có một máy trên mỗi dòng và dấu phẩy để tách các bản ghi. Đoạn mã sau phân tích tên máy trên mỗi dòng và để lại danh sách các giá trị được phân tách bằng dấu phẩy values.

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

Đoạn mã cụ thể bash sau đây đặt các giá trị trong một mảng.

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

@Giles: Cảm ơn bạn đã gợi ý. Có phải cũng có thể lấy được tổng số tệp cho mỗi máy không? nghĩa là tổng số sử dụng cùng một lệnh trên? Giống như, ví dụ ở trên, machineA có bốn tệp và machineB cũng có bốn tệp
SSH

@SSH Xem chỉnh sửa của tôi.
Gilles

0

Bạn có thể sử dụng awkđể hoàn thành nhiệm vụ.

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]

Cảm ơn John. Có thể lấy tổng số tệp cho mỗi máy. Giống như, ví dụ trên, machineA có bốn tệp và machineB cũng có bốn tệp. Có thể có được điều đó là tốt?
SSH

0

Cái này trông hơi giống JSON. Bạn có thể sửa nó thành JSON thích hợp và sử dụng các công cụ 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"
   ]
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.