อาร์เรย์หลายมิติใน Bash

ฉันกำลังวางแผนสคริปต์เพื่อจัดการระบบ Linux บางส่วนของฉัน และอยู่ในจุดที่ต้องตัดสินใจว่าต้องการใช้ ทุบตี หรือ หลาม.

ฉันต้องการทำเช่นนี้เป็นสคริปต์ Bash เพียงเพราะคำสั่งนั้นง่ายกว่า แต่ปัจจัยในการตัดสินใจที่แท้จริงคือการกำหนดค่า ฉันจำเป็นต้องจัดเก็บอาร์เรย์หลายมิติในไฟล์กำหนดค่าเพื่อบอกสคริปต์ว่าต้องทำอย่างไรกับตัวมันเอง การจัดเก็บคู่คีย์=ค่าแบบง่ายในไฟล์กำหนดค่านั้นง่ายพอด้วย bash แต่วิธีเดียวที่ฉันคิดว่าจะทำอาเรย์หลายมิติได้คือกลไกการแยกวิเคราะห์สองชั้น บางอย่างเช่น

array=&d1|v1;v2;v3&d2|v1;v2;v3

แต่รหัส marshall/unmarshall อาจเป็นหมีได้และมันยังห่างไกลจากการเป็นมิตรกับผู้ใช้สำหรับ SAP ที่ไม่ดีตัวต่อไปที่ต้องจัดการสิ่งนี้ หากฉันไม่สามารถทำสิ่งนี้ได้อย่างง่ายดายใน bash ฉันจะเขียนการกำหนดค่าลงในไฟล์ xml และเขียนสคริปต์ใน python

มีวิธีง่าย ๆ ในการทำเช่นนี้ใน bash หรือไม่?

ขอบคุณทุกคน


person scphantm    schedule 27.06.2012    source แหล่งที่มา
comment
ที่เกี่ยวข้อง: วิธีประกาศอาร์เรย์ 2D ใน bash   -  person kenorb    schedule 29.09.2015


คำตอบ (13)


Bash ไม่รองรับอาร์เรย์หลายมิติหรือแฮช และดูเหมือนว่าคุณต้องการแฮชที่มีค่าเป็นอาร์เรย์ โซลูชันนี้ไม่สวยงามมาก โซลูชันที่มีไฟล์ xml น่าจะดีกว่า:

array=('d1=(v1 v2 v3)' 'd2=(v1 v2 v3)')
for elt in "${array[@]}";do eval $elt;done
echo "d1 ${#d1[@]} ${d1[@]}"
echo "d2 ${#d2[@]} ${d2[@]}"

แก้ไข: คำตอบนี้ค่อนข้างเก่าเนื่องจากเนื่องจาก bash 4 รองรับตารางแฮชโปรดดู คำตอบนี้ สำหรับวิธีแก้ปัญหาที่ไม่มีการประเมิน

person Nahuel Fouilleul    schedule 27.06.2012
comment
นั่นคือคำตอบที่ฉันคาดหวัง แต่ก็ไม่กล้าที่จะถาม ขอบคุณ - person scphantm; 28.06.2012
comment
เพียงแค่บันทึก bash รองรับแฮช (อาร์เรย์ที่เชื่อมโยง) เริ่มตั้งแต่เวอร์ชัน 4 ข้อมูลเพิ่มเติม: mywiki.wooledge.org/BashFAQ/ 006 - person poncha; 21.04.2013
comment
FYI associative arrays สามารถตั้งค่าแอตทริบิวต์ 'ประกาศ' บางอย่างได้ เช่น 'ตั้งค่าเป็นตัวพิมพ์ใหญ่ในการมอบหมาย' อย่างไรก็ตาม ไม่สามารถตั้งค่าอาร์เรย์ -A [associative] หรือ -a [ตัวเลข] ให้กับพวกเขาได้ หรือ - ชุดการอ้างอิง n แต่คุณสามารถแท็กชื่อตัวแปรของคุณด้วยตัวเลขได้ และใช้ตัวแปรแทน: MYARR_$i_[$j] จะเป็นสิ่งที่ใกล้เคียงที่สุด อย่างไรก็ตาม มันไม่ใช่อาร์เรย์ md จริง แต่นี่เป็นสิ่งที่ดีที่สุด คุณกำลังจะได้รับ คุณสามารถใช้ฟังก์ชั่นเป็นระบบหลอกอาเรย์ได้หากคุณหมดหวังมากพอ :) - person osirisgothra; 02.05.2014
comment
และนั่นก็คือ... ชั่วร้าย... ขอโทษที ฉันหมายถึง eval - person Angry 84; 31.12.2015
comment
คุณสามารถ declare -a "$elt" แทน eval $elt ได้ตลอดเวลา - person ghoti; 20.07.2017
comment
@ghoti ใช่คำตอบค่อนข้างเก่า อีกคำตอบที่คล้ายกัน stackoverflow.com/a/44831174/1454708 ฉันคิดว่า OP รู้ว่าเขากำลังทำอะไรอยู่และมีการตรวจสอบอินพุตแล้ว - person Nahuel Fouilleul; 20.07.2017

Bash ไม่มีอาร์เรย์หลายมิติ แต่คุณสามารถจำลองเอฟเฟกต์ที่ค่อนข้างคล้ายกันกับอาเรย์แบบเชื่อมโยงได้ ต่อไปนี้เป็นตัวอย่างของอาเรย์แบบเชื่อมโยงที่แกล้งทำเป็นอาเรย์หลายมิติ:

declare -A arr
arr[0,0]=0
arr[0,1]=1
arr[1,0]=2
arr[1,1]=3
echo "${arr[0,0]} ${arr[0,1]}" # will print 0 1

หากคุณไม่ประกาศอาร์เรย์เป็นแบบเชื่อมโยง (ด้วย -A) การดำเนินการข้างต้นจะไม่ทำงาน ตัวอย่างเช่น หากคุณละบรรทัด declare -A arr ไว้ echo จะพิมพ์ 2 3 แทน 0 1 เนื่องจาก 0,0, 1,0 และดังกล่าวจะถือเป็นนิพจน์ทางคณิตศาสตร์และประเมินเป็น 0 (ค่าทางด้านขวาของตัวดำเนินการลูกน้ำ)

person Jahid    schedule 17.07.2015
comment
ประกาศ -A [arrayname] ล้วงเอาตัวเลือกที่ไม่ถูกต้อง อย่างน้อยใน OS X - person ktappe; 14.10.2016
comment
แก้ไข: ดูเหมือนว่า OS X จะมาพร้อมกับ bash 3 และตัวเลือกนี้ต้องใช้ bash 4 ขอบคุณ Apple - person ktappe; 14.10.2016

ใช้งานได้ด้วย 1. การขยายทางอ้อมด้วย ! ซึ่งเพิ่มการอ้อมหนึ่งเลเยอร์ และ 2. การขยายสตริงย่อยซึ่งมีพฤติกรรมแตกต่างกับอาร์เรย์ และสามารถใช้เพื่อแบ่งส่วนตามที่อธิบายไว้ https://stackoverflow.com/a/1336245/317623

# Define each array and then add it to the main one
SUB_0=("name0" "value 0")
SUB_1=("name1" "value;1")
MAIN_ARRAY=(
  SUB_0[@]
  SUB_1[@]
)

# Loop and print it.  Using offset and length to extract values
COUNT=${#MAIN_ARRAY[@]}
for ((i=0; i<$COUNT; i++))
do
  NAME=${!MAIN_ARRAY[i]:0:1}
  VALUE=${!MAIN_ARRAY[i]:1:1}
  echo "NAME ${NAME}"
  echo "VALUE ${VALUE}"
done

มีพื้นฐานมาจากคำตอบนี้ที่นี่

person Paul Sheldrake    schedule 03.02.2015
comment
มันควรจะเป็น NAME=${!MAIN_ARRAY[i]:0:1} และ VALUE=${!MAIN_ARRAY[i]:1:1} ถ้าจำไม่ผิดว่ามันได้ผล (เช่น แลก URLS เป็น MAIN) - person Christian; 27.10.2015
comment
ฉันยังไม่ได้พิจารณาสาเหตุ - แต่ใช้งานได้ดีบน bash v4.3+ แต่มีปัญหากับเวอร์ชันก่อนหน้า (พิมพ์อักขระแต่ละตัวแทนคำเต็ม) - person dtmland; 17.05.2019
comment
ฉันหวังว่า with จะทำงานโดยไม่ต้องกำหนด SUB_0 และ SUB_1 ก่อน MAIN_ARRAY - person freethebees; 12.11.2019
comment
@dtmland สิ่งที่ดูเหมือนจะเกิดขึ้นกับ bash เวอร์ชันเก่าคือการขยายสตริงย่อยธรรมดาแทนที่จะเป็นทางเลือกในการแบ่งอาเรย์ - person MarcH; 02.07.2020

โดยไม่ขึ้นอยู่กับเชลล์ที่ใช้ (sh, ksh, bash, ... ) วิธีการต่อไปนี้ใช้งานได้ค่อนข้างดีสำหรับอาร์เรย์ n มิติ (ตัวอย่างครอบคลุมอาร์เรย์ 2 มิติ)

ในตัวอย่าง ตัวคั่นบรรทัด (มิติที่ 1) คืออักขระเว้นวรรค สำหรับการแนะนำตัวคั่นฟิลด์ (มิติที่ 2) จะใช้เครื่องมือยูนิกซ์มาตรฐาน tr ตัวคั่นเพิ่มเติมสำหรับมิติเพิ่มเติมสามารถใช้ในลักษณะเดียวกันได้

แน่นอนว่าประสิทธิภาพของแนวทางนี้ไม่ค่อยดีนัก แต่ถ้าประสิทธิภาพไม่ใช่เกณฑ์ แนวทางนี้ก็ค่อนข้างทั่วไปและสามารถแก้ปัญหาได้หลายอย่าง:

array2d="1.1:1.2:1.3 2.1:2.2 3.1:3.2:3.3:3.4"

function process2ndDimension {
    for dimension2 in $*
    do
        echo -n $dimension2 "   "
    done
    echo
}

function process1stDimension {
    for dimension1 in $array2d
    do
        process2ndDimension `echo $dimension1 | tr : " "`
    done
}

process1stDimension

ผลลัพธ์ของตัวอย่างนั้นมีลักษณะดังนี้:

1.1     1.2     1.3     
2.1     2.2     
3.1     3.2     3.3     3.4 
person yaccob    schedule 05.10.2013
comment
แต่จะทำงานได้ไม่ดีนักหากค่ามีการเว้นวรรคหรือทวิภาค :) - person dannysauer; 01.06.2016
comment
@dannysauer - ควรจะใช้ได้ถ้าคุณเข้ารหัสช่องว่างและโคลอนเป็นเอนทิตีหรือรหัสฐานสิบหก ตัวอย่างเช่น %20 และ %3a ควรจะง่ายพอที่จะประมวลผลอินพุตและเอาท์พุตจากอาเรย์ผ่านฟังก์ชันดังกล่าวหากนั่นเป็นปัญหาสำหรับคุณ - person ghoti; 13.09.2016
comment
ดังนั้นค่าต่างๆ จึงไม่มีการเว้นวรรคหรือโคลอน :D - person dannysauer; 13.09.2016
comment
ฉันสามารถมีหนึ่งลูปและหนึ่งฟังก์ชันแทนที่จะเป็นสองฟังก์ชันได้หรือไม่ - person Timo; 07.06.2021

หากคุณต้องการใช้สคริปต์ทุบตีและอ่านง่าย แนะนำให้วางข้อมูลใน JSON ที่มีโครงสร้าง จากนั้นใช้ lightweight tool jq ในคำสั่ง bash ของคุณเพื่อวนซ้ำผ่านอาร์เรย์ ตัวอย่างเช่นกับชุดข้อมูลต่อไปนี้:

[

    {"specialId":"123",
    "specialName":"First"},

    {"specialId":"456",
    "specialName":"Second"},

    {"specialId":"789",
    "specialName":"Third"}
]

คุณสามารถวนซ้ำข้อมูลนี้ด้วยสคริปต์ทุบตีและ jq ดังนี้:

function loopOverArray(){

    jq -c '.[]' testing.json | while read i; do
        # Do stuff here
        echo "$i"
    done
}

loopOverArray

เอาท์พุต:

{"specialId":"123","specialName":"First"}
{"specialId":"456","specialName":"Second"}
{"specialId":"789","specialName":"Third"}
person Dustin    schedule 15.06.2019
comment
ขอบคุณสำหรับสิ่งนี้. เป็นวิธีการแก้ปัญหาเชิงปฏิบัติที่ง่ายที่สุดอย่างแน่นอน เราฝากความบริสุทธิ์ไว้กับผู้พิถีพิถันได้ - person Martin Bramwell; 17.07.2020

หลังจากการลองผิดลองถูกมาหลายครั้ง ฉันพบว่าอาร์เรย์หลายมิติที่ดีที่สุด ชัดเจนที่สุด และง่ายที่สุดบน bash คือการใช้ var ปกติ ใช่.

ข้อดี: คุณไม่จำเป็นต้องวนซ้ำอาร์เรย์ขนาดใหญ่ คุณสามารถ echo "$var" และใช้ grep/awk/sed ได้ ง่ายและชัดเจน และคุณสามารถมีคอลัมน์ได้มากเท่าที่คุณต้องการ

ตัวอย่าง:

$ var=$(echo -e 'kris hansen oslo\nthomas jonson peru\nbibi abu johnsonville\njohnny lipp peru')

$ echo "$var"
kris hansen oslo
thomas johnson peru
bibi abu johnsonville
johnny lipp peru

หากคุณต้องการค้นหาทุกคนในเปรู

$ echo "$var" | grep peru
thomas johnson peru
johnny lipp peru

เฉพาะ grep(sed) ในฟิลด์ที่สาม

$ echo "$var" | sed -n -E '/(.+) (.+) peru/p'
thomas johnson peru
johnny lipp peru

หากคุณต้องการเพียงฟิลด์ x

$ echo "$var" | awk '{print $2}'
hansen
johnson
abu
johnny

ทุกคนในเปรูเรียกว่าโทมัส และแค่คืนนามสกุลของเขา

$ echo "$var" |grep peru|grep thomas|awk '{print $2}'
johnson

ข้อสงสัยใด ๆ ที่คุณนึกถึง... ง่ายมาก

หากต้องการเปลี่ยนรายการ:

$ var=$(echo "$var"|sed "s/thomas/pete/")

หากต้องการลบแถวที่มี "x"

$ var=$(echo "$var"|sed "/thomas/d")

หากต้องการเปลี่ยนฟิลด์อื่นในแถวเดียวกันโดยยึดตามค่าจากรายการอื่น

$ var=$(echo "$var"|sed -E "s/(thomas) (.+) (.+)/\1 test \3/")
$ echo "$var"
kris hansen oslo                                                                                                                                             
thomas test peru                                                                                                                                          
bibi abu johnsonville
johnny lipp peru

แน่นอนว่าการวนซ้ำก็ใช้ได้เช่นกันหากคุณต้องการทำเช่นนั้น

$ for i in "$var"; do echo "$i"; done
kris hansen oslo
thomas jonson peru
bibi abu johnsonville
johnny lipp peru

สิ่งเดียวที่พบได้ในสิ่งนี้คือคุณ ต้อง อ้างอิง var เสมอ (ในตัวอย่าง; ทั้ง var และ i) ไม่เช่นนั้นสิ่งต่าง ๆ จะมีลักษณะเช่นนี้

$ for i in "$var"; do echo $i; done
kris hansen oslo thomas jonson peru bibi abu johnsonville johnny lipp peru

และบางคนจะพูดอย่างไม่ต้องสงสัยว่ามันจะไม่ทำงานถ้าคุณมีช่องว่างในอินพุตของคุณ อย่างไรก็ตามสามารถแก้ไขได้โดยใช้เดลิมิเตอร์อื่นในอินพุตของคุณ เช่น (ใช้อักขระ utf8 ทันทีเพื่อเน้นว่าคุณสามารถเลือกบางสิ่งที่อินพุตของคุณไม่มี มี แต่คุณสามารถเลือกอะไรก็ได้):

$ var=$(echo -e 'field one☥field two hello☥field three yes moin\nfield 1☥field 2☥field 3 dsdds aq')

$ for i in "$var"; do echo "$i"; done
field one☥field two hello☥field three yes moin
field 1☥field 2☥field 3 dsdds aq

$ echo "$var" | awk -F '☥' '{print $3}'
field three yes moin
field 3 dsdds aq

$ var=$(echo "$var"|sed -E "s/(field one)☥(.+)☥(.+)/\1☥test☥\3/")
$ echo "$var"
field one☥test☥field three yes moin
field 1☥field 2☥field 3 dsdds aq

หากคุณต้องการจัดเก็บบรรทัดใหม่ในอินพุตของคุณ คุณสามารถแปลงบรรทัดใหม่เป็นอย่างอื่นก่อนอินพุตและแปลงกลับอีกครั้งในเอาต์พุต (หรืออย่าใช้ bash...) สนุก!

person n00p    schedule 25.04.2017
comment
สิ่งนี้อาจใช้ได้กับสองมิติ แต่สำหรับกรณีทั่วไปที่มี n มิติ นี่เริ่มจะยากขึ้น - person U. Windl; 16.11.2017
comment
นั่นเป็นเรื่องจริง เมื่อฉันเขียนสิ่งนี้ ฉันไม่เห็นความแตกต่างระหว่าง 2d, 3d, 4d และทั้งหมดที่ฉันต้องการก็คือการสนับสนุน/db แบบตารางที่ดี ตอนนี้ ฉันยังคงไม่ต้องการอะไรมากไปกว่า 2d แต่ถ้าใครต้องการมากกว่านี้ ฉันคิดว่ามันคงไม่ได้ผล - person n00p; 17.11.2017

ขยายคำตอบของ Paul - นี่คือเวอร์ชันของฉันในการทำงานกับอาร์เรย์ย่อยแบบเชื่อมโยงใน bash:

declare -A SUB_1=(["name1key"]="name1val" ["name2key"]="name2val")
declare -A SUB_2=(["name3key"]="name3val" ["name4key"]="name4val")
STRING_1="string1val"
STRING_2="string2val"
MAIN_ARRAY=(
  "${SUB_1[*]}"
  "${SUB_2[*]}"
  "${STRING_1}"
  "${STRING_2}"
)
echo "COUNT: " ${#MAIN_ARRAY[@]}
for key in ${!MAIN_ARRAY[@]}; do
    IFS=' ' read -a val <<< ${MAIN_ARRAY[$key]}
    echo "VALUE: " ${val[@]}
    if [[ ${#val[@]} -gt 1 ]]; then
        for subkey in ${!val[@]}; do
            subval=${val[$subkey]}
            echo "SUBVALUE: " ${subval}
        done
    fi
done

ใช้งานได้กับค่าผสมในอาร์เรย์หลัก - strings/arrays/assoc อาร์เรย์

สิ่งสำคัญที่นี่คือการรวมอาร์เรย์ย่อยด้วยเครื่องหมายคำพูดเดี่ยว และใช้ * แทน @ เมื่อจัดเก็บอาร์เรย์ย่อยภายในอาร์เรย์หลัก เพื่อให้จัดเก็บเป็นสตริงเดี่ยวที่คั่นด้วยช่องว่าง: "${SUB_1[*]}"

จากนั้นทำให้ง่ายต่อการแยกวิเคราะห์อาร์เรย์ออกจากค่านั้นเมื่อวนซ้ำค่าด้วย IFS=' ' read -a val <<< ${MAIN_ARRAY[$key]}

รหัสด้านบนออก:

COUNT:  4
VALUE:  name1val name2val
SUBVALUE:  name1val
SUBVALUE:  name2val
VALUE:  name4val name3val
SUBVALUE:  name4val
SUBVALUE:  name3val
VALUE:  string1val
VALUE:  string2val
person Vigintas Labakojis    schedule 12.02.2015
comment
ดังนั้นคุณจึงไม่สามารถจัดเก็บสตริงที่มีการเว้นวรรคได้ (ไม่ได้กล่าวถึงการขึ้นบรรทัดใหม่) การที่คุณไม่มีเครื่องหมายคำพูดทำให้ไม่สามารถมีอักขระ glob ได้อย่างปลอดภัย - person gniourf_gniourf; 12.02.2015
comment
ใช่ ฉันเพิ่งรู้ว่ามันจะแบ่งสตริงที่มีช่องว่างออกเป็นค่าย่อยที่แยกจากกัน แต่โดยส่วนตัวแล้วฉันสามารถอยู่กับสิ่งนั้นได้หากเป็นเพียงค่ากำหนดค่า... แต่ฉันคิดว่านั่นเป็นสิ่งที่ดีที่สุดที่เราสามารถทำได้ใน bash ฉันเปิดรับข้อเสนอแนะ / การปรับปรุง เพิ่มคำพูดขอบคุณสำหรับการชี้ให้เห็น :) - person Vigintas Labakojis; 12.02.2015
comment
สวัสดี คุณช่วยอธิบายหน่อยได้ไหมว่า IFS=' ' read -a val <<< ${MAIN_ARRAY[$key]} ทำงานอย่างไร ไอเอฟเอสคืออะไร? มีการกำหนดตัวแปรอยู่ที่นั่นหรือไม่? เหตุใด read จึงถูกผนวกเข้ากับคำจำกัดความของตัวแปร - person Boyang; 09.02.2016
comment
@ชาร์ลส์ดับบลิว ดูคู่มืออ้างอิง Bash, 5.1 ตัวแปรเชลล์เป้าหมาย: IFS – รายการอักขระที่แยกฟิลด์ ใช้เมื่อเชลล์แยกคำซึ่งเป็นส่วนหนึ่งของการขยาย และ 6.7 Arrays: หากคำนั้นใช้เครื่องหมายคำพูดคู่ ${name[*]} ขยายเป็นคำเดียวโดยมีค่าของสมาชิกอาร์เรย์แต่ละตัวคั่นด้วยอักขระตัวแรกของตัวแปร IFS, .... - person Gerold Broser; 08.06.2016

พบคำตอบมากมายที่นี่สำหรับการสร้างอาร์เรย์หลายมิติใน bash

และไม่มีข้อยกเว้นทั้งหมดล้วนแต่ซับซ้อนและใช้งานยาก

หากอาร์เรย์ MD เป็นเกณฑ์ที่กำหนด ก็ถึงเวลาที่ต้องตัดสินใจ:

ใช้ภาษาที่รองรับอาร์เรย์ MD

ความชอบของฉันคือ Perl คนส่วนใหญ่คงจะเลือก Python ไม่ว่าจะทำงาน.

จัดเก็บข้อมูลไว้ที่อื่น

มีการแนะนำ JSON และ jq แล้ว XML ได้รับการแนะนำเช่นกัน แต่สำหรับการใช้งานของคุณ JSON และ jq น่าจะง่ายกว่า

ดูเหมือนว่า Bash อาจไม่ใช่ตัวเลือกที่ดีที่สุดสำหรับสิ่งที่คุณต้องทำ

บางครั้งคำถามที่ถูกต้องไม่ใช่ "ฉันจะทำ X ในเครื่องมือ Y ได้อย่างไร" แต่เป็น "เครื่องมือใดดีที่สุดที่จะทำ X"

person Jared Still    schedule 03.09.2019

ฉันกำลังโพสต์สิ่งต่อไปนี้เพราะมันเป็นวิธีที่ง่ายและชัดเจนในการเลียนแบบ (อย่างน้อยก็ในระดับหนึ่ง) พฤติกรรมของอาร์เรย์สองมิติใน Bash มันใช้ไฟล์ที่นี่ (ดูคู่มือ Bash) และ read (คำสั่ง Bash ในตัว):

## Store the "two-dimensional data" in a file ($$ is just the process ID of the shell, to make sure the filename is unique)
cat > physicists.$$ <<EOF
Wolfgang Pauli 1900
Werner Heisenberg 1901
Albert Einstein 1879
Niels Bohr 1885
EOF
nbPhysicists=$(wc -l physicists.$$ | cut -sf 1 -d ' ')     # Number of lines of the here-file specifying the physicists.

## Extract the needed data
declare -a person     # Create an indexed array (necessary for the read command).                                                                                 
while read -ra person; do
    firstName=${person[0]}
    familyName=${person[1]}
    birthYear=${person[2]}
    echo "Physicist ${firstName} ${familyName} was born in ${birthYear}"
    # Do whatever you need with data
done < physicists.$$

## Remove the temporary file
rm physicists.$$

เอาท์พุต: Physicist Wolfgang Pauli was born in 1900 Physicist Werner Heisenberg was born in 1901 Physicist Albert Einstein was born in 1879 Physicist Niels Bohr was born in 1885

วิธีการทำงาน:

  • บรรทัดในไฟล์ชั่วคราวที่สร้างขึ้นมีบทบาทเป็นเวกเตอร์หนึ่งมิติ โดยที่ช่องว่าง (หรืออักขระการแยกใดๆ ก็ตามที่คุณเลือก ดูคำอธิบายของคำสั่ง read ในคู่มือ Bash) แยกองค์ประกอบของเวกเตอร์เหล่านี้
  • จากนั้น เมื่อใช้คำสั่ง read พร้อมด้วยตัวเลือก -a เราจะวนซ้ำแต่ละบรรทัดของไฟล์ (จนกว่าเราจะไปถึงจุดสิ้นสุดของไฟล์) สำหรับแต่ละบรรทัด เราสามารถกำหนดฟิลด์ที่ต้องการ (= คำ) ให้กับอาร์เรย์ ซึ่งเราประกาศก่อนการวนซ้ำ ตัวเลือก -r ของคำสั่ง read ป้องกันไม่ให้แบ็กสแลชทำหน้าที่เป็นอักขระหลีก ในกรณีที่เราพิมพ์แบ็กสแลชในเอกสารที่นี่ physicists.$$

โดยสรุป ไฟล์จะถูกสร้างขึ้นเป็นอาร์เรย์ 2 มิติ และองค์ประกอบต่างๆ จะถูกแยกออกโดยใช้การวนซ้ำในแต่ละบรรทัด และใช้ความสามารถของคำสั่ง read เพื่อกำหนดคำให้กับองค์ประกอบของอาร์เรย์ (จัดทำดัชนี)

การปรับปรุงเล็กน้อย:

ในโค้ดข้างต้น ไฟล์ physicists.$$ ถูกกำหนดให้เป็นอินพุตของลูป while ดังนั้นในความเป็นจริงแล้วไฟล์ดังกล่าวจึงถูกส่งผ่านไปยังคำสั่ง read อย่างไรก็ตาม ฉันพบว่าสิ่งนี้ทำให้เกิดปัญหาเมื่อฉันมีคำสั่งอื่นที่ขออินพุตภายในลูป while ตัวอย่างเช่น คำสั่ง select รออินพุตมาตรฐาน และหากวางไว้ภายในลูป while คำสั่งก็จะรับอินพุตจาก physicists.$$ แทนที่จะแสดงพร้อมต์ในบรรทัดคำสั่งสำหรับการป้อนข้อมูลของผู้ใช้ เพื่อแก้ไขปัญหานี้ ฉันใช้ตัวเลือก -u ของ read ซึ่งอนุญาตให้อ่านจากตัวอธิบายไฟล์ เราเพียงแค่ต้องสร้าง file descriptor (ด้วยคำสั่ง exec) ที่สอดคล้องกับ physicists.$$ และกำหนดให้ตัวเลือก -u อ่าน ดังเช่นในโค้ดต่อไปนี้:

## Store the "two-dimensional data" in a file ($$ is just the process ID of the shell, to make sure the filename is unique)
cat > physicists.$$ <<EOF
Wolfgang Pauli 1900
Werner Heisenberg 1901
Albert Einstein 1879
Niels Bohr 1885
EOF
nbPhysicists=$(wc -l physicists.$$ | cut -sf 1 -d ' ')     # Number of lines of the here-file specifying the physicists.
exec {id_file}<./physicists.$$     # Create a file descriptor stored in 'id_file'.

## Extract the needed data
declare -a person     # Create an indexed array (necessary for the read command).                                                                                 
while read -ra person -u "${id_file}"; do
firstName=${person[0]}
familyName=${person[1]}
birthYear=${person[2]}
echo "Physicist ${firstName} ${familyName} was born in ${birthYear}"
# Do whatever you need with data
done

## Close the file descriptor
exec {id_file}<&-
## Remove the temporary file
rm physicists.$$

ขอให้สังเกตว่าตัวอธิบายไฟล์ถูกปิดในตอนท้าย

person Giuseppe    schedule 27.09.2017
comment
มันเหมือนกับโซลูชันของ user7909577 มาก อย่างไรก็ตาม สิ่งที่คุณทำคือเขียนลำดับบรรทัดในไฟล์ข้อความ ดังนั้นมิติที่ 1 คือหมายเลขบรรทัด มิติที่ 2 คือคำในบรรทัด อย่างไรก็ตาม โซลูชันทั่วไปอย่างแท้จริงจะอนุญาตให้ใส่อาร์เรย์ใดก็ได้ในอาร์เรย์ใดก็ได้ และนั่นจะไม่ทำงาน ตกลง คุณสามารถใช้มิติได้โดยการเยื้องเส้น แต่ก็ยังไม่สวยงามมากนัก - person U. Windl; 16.11.2017
comment
@ยู. Windi ใส่อาร์เรย์ใด ๆ ลงในอาร์เรย์ใดก็ได้ และนั่นไม่ได้ผล: นั่นเป็นเหตุผลที่ฉันเน้นในคำตอบของฉันว่าเป็นการจำลอง 2D-ARRAYS มันเหมือนกับวิธีแก้ปัญหาของ user7909577 มาก: ฉันไม่คิดอย่างนั้นเขา / เธอแยกวิเคราะห์สตริงด้วย sed/grep/awk ในคำตอบของเขา และฉันทำโดยการแปลงสตริงเป็นอาร์เรย์ก่อน จากนั้นใช้ดัชนี (นั่น คือสาเหตุที่มันจำลองพฤติกรรมของอาเรย์) - person Giuseppe; 16.11.2017
comment
@Guiseppe: แต่โซลูชันทั้งสองใช้ไฟล์ข้อความเป็นตัวแทนของอาร์เรย์สองมิติที่มีขนาดตามที่ฉันอธิบายไว้ คำถามเฉพาะเกี่ยวกับอาร์เรย์ หลาย มิติ (มีคำถามแยกต่างหากสำหรับอาร์เรย์สองมิติ) - person U. Windl; 16.11.2017
comment
@ยู. Windl: ฉันเห็นด้วย แต่เมื่อฉันเข้าใจวิธีแก้ปัญหาของ user7909577 มันไม่เคยเป็นอาร์เรย์ที่คุณมีเลย แต่เป็นรายการยาวที่คุณต้องใส่ตัวคั่น (เช่นขึ้นบรรทัดใหม่) ดังนั้นแต่ละครั้งที่คุณเริ่มลูปใหม่ คุณจะติดอยู่กับองค์ประกอบเดียวในรายการยาวๆ ของคุณ ตรงกันข้ามกับโซลูชันของฉัน ในแต่ละลูป คุณทำงานกับอาร์เรย์ 1D จริง (เส้นของอาร์เรย์ 2D จำลอง) และคุณจึงสามารถเข้าถึงองค์ประกอบใด ๆ ของมันได้ ซึ่งสะดวกกว่ามากในความคิดของฉัน ฉันไม่ได้สังเกตเห็นคำถามอื่นเกี่ยวกับอาร์เรย์สองมิติ ดังนั้นหากคุณรู้วิธีย้ายคำตอบ ฉันก็สนใจ - person Giuseppe; 16.11.2017

ฉันทำสิ่งนี้โดยใช้ อาร์เรย์ที่เชื่อมโยง ตั้งแต่ bash 4 และตั้งค่า IFS เป็นค่าที่สามารถกำหนดได้ด้วยตนเอง

วัตถุประสงค์ของแนวทางนี้คือเพื่อให้อาร์เรย์เป็นค่าของคีย์อาร์เรย์ที่เชื่อมโยง

หากต้องการตั้งค่า IFS กลับเป็นค่าเริ่มต้น ให้ยกเลิกการตั้งค่า

  • unset IFS

นี่คือตัวอย่าง:

#!/bin/bash

set -euo pipefail

# used as value in asscciative array
test=(
  "x3:x4:x5"
)
# associative array
declare -A wow=(
  ["1"]=$test
  ["2"]=$test
)
echo "default IFS"
for w in ${wow[@]}; do
  echo "  $w"
done

IFS=:
echo "IFS=:"
for w in ${wow[@]}; do
  for t in $w; do
    echo "  $t"
  done
done
echo -e "\n or\n"
for w in ${!wow[@]}
do
  echo "  $w"
  for t in ${wow[$w]}
  do
    echo "    $t"
  done
done

unset IFS
unset w
unset t
unset wow
unset test

ผลลัพธ์ของสคริปต์ด้านล่างคือ:

default IFS
  x3:x4:x5
  x3:x4:x5
IFS=:
  x3
  x4
  x5
  x3
  x4
  x5

 or

  1
    x3
    x4
    x5
  2
    x3
    x4
    x5
person rocksteady    schedule 30.06.2017
comment
ฉันไม่เห็นเหตุผลที่จะใช้ test=("x3:x4:x5") เมื่อคุณอ้างถึงเป็น $test: คุณก็สามารถทำได้ test="x3:x4:x5" - person U. Windl; 16.11.2017
comment
ใช่ คุณพูดถูกจริงๆ ขอบคุณสำหรับคำใบ้ - person rocksteady; 18.11.2017

Bash ไม่รองรับอาร์เรย์หลายมิติ แต่เราสามารถใช้งานได้โดยใช้ Associate array ที่นี่ดัชนีเป็นกุญแจสำคัญในการดึงค่า Associate Array มีอยู่ใน bash เวอร์ชัน 4

#!/bin/bash

declare -A arr2d
rows=3
columns=2

for ((i=0;i<rows;i++)) do
    for ((j=0;j<columns;j++)) do
        arr2d[$i,$j]=$i
    done
done


for ((i=0;i<rows;i++)) do
    for ((j=0;j<columns;j++)) do
        echo ${arr2d[$i,$j]}
    done
done
person rashok    schedule 23.04.2018

ฉันมีวิธีแก้ปัญหาที่ค่อนข้างเรียบง่ายแต่ชาญฉลาด: เพียงกำหนดอาร์เรย์ด้วยตัวแปรในชื่อ ตัวอย่างเช่น:

for (( i=0 ; i<$(($maxvalue + 1)) ; i++ ))
  do
  for (( j=0 ; j<$(($maxargument + 1)) ; j++ ))
    do
    declare -a array$i[$j]=((Your rule))
  done
done

ไม่รู้ว่าสิ่งนี้ช่วยได้หรือไม่เนื่องจากไม่ใช่สิ่งที่คุณขอ แต่มันได้ผลสำหรับฉัน (สามารถทำได้เช่นเดียวกันกับตัวแปรที่ไม่มีอาร์เรย์)

person testiner    schedule 18.01.2018

person    schedule
comment
ลองเพิ่มคำอธิบายเกี่ยวกับสิ่งที่โค้ดควรทำดูไหม แม้ว่าการจัดรูปแบบบางอย่างจะมีประโยชน์เช่นกัน แต่ความคิดเห็นบางรายการก็จะปรับปรุงคำตอบให้ดียิ่งขึ้นไปอีก แม้ว่าฉันจะเห็นหลายอาร์เรย์ได้ แต่ฉันไม่เห็นอาร์เรย์หลายมิติที่นี่ - person Izzy; 06.11.2014