แยกไฟล์และวางไว้ในโครงสร้างข้อมูลที่สอดคล้องกัน?


10

ฉันมีไฟล์ที่มีบรรทัดเดียวด้านล่าง:

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

ที่ฉันมีสองชุดข้อมูล:

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

ตอนนี้ฉันต้องอ่านไฟล์ข้างต้นและแยกมันในแบบที่ฉันสามารถดึงข้อมูลของเครื่องแต่ละเครื่องตามที่กล่าวไว้ข้างต้นและเก็บไว้ในโครงสร้างข้อมูลบางอย่าง

ขณะนี้ฉันสับสนโครงสร้างข้อมูลที่ฉันควรใช้ใน bash shell script ถ้าฉันทำสิ่งนี้ใน Java ฉันจะใช้Map<String, Set<String>>แต่ฉันไม่แน่ใจว่าฉันควรใช้อะไรในเชลล์สคริปต์

และหลังจากเก็บไว้ในโครงสร้างข้อมูลฉันต้องวนซ้ำและพิมพ์ผลลัพธ์

ฉันสามารถอ่านไฟล์ด้านบนโดยใช้เชลล์สคริปต์ด้านล่าง:

#!/bin/bash

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

แต่ฉันไม่แน่ใจว่าจะแยกข้อมูลบรรทัดข้างต้นด้วยวิธีดังกล่าวอย่างไรเพื่อให้ฉันสามารถดึงข้อมูลแต่ละเครื่องจากนั้นจัดเก็บไว้ในโครงสร้างข้อมูลบางส่วนได้

UPDATE: -

ด้านล่างนี้คือเชลล์สคริปต์ที่ฉันได้รับหลังจากทำตามคำแนะนำที่ได้รับจากเกล็น -

#!/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 นั้นเป็นพื้นฐานเกินไปสำหรับแม้แต่อาร์เรย์ 2 มิติ คุณแน่ใจหรือว่านี่เป็นเครื่องมือที่คุณต้องการใช้?

ด้วย 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
                        ]
        };

ขอบคุณสำหรับคำแนะนำ ฉันอาจจะใช้ตัวเลือก shell script ในที่สุดฉันก็จำเป็นต้องใช้ scp ดังนั้นฉันเชื่อว่าการทำ scp ใน shell script นั้นจะง่าย แต่เอาล่ะลองมาดูกันว่ามันจะออกมาอย่างไร ฉันได้อัปเดตคำถามของฉันด้วยเชลล์สคริปต์จริงที่ฉันอาจใช้หลังจากรวมคำแนะนำของคุณ โปรดดูและแจ้งให้เราทราบหากมันถูกต้องและหากมีสิ่งใดที่คุณต้องการแก้ไขแล้วแจ้งให้เราทราบเช่นกัน
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 

ตัวอย่างข้อมูลเฉพาะทุบตีต่อไปนี้จะใส่ค่าลงในอาร์เรย์

 | 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 ดูการแก้ไขของฉัน
Gilles 'หยุดความชั่วร้าย'

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 มีสี่ไฟล์ เป็นไปได้ที่จะได้รับเช่นกัน?
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"
   ]
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.