นับจำนวนองค์ประกอบในอาร์เรย์ทุบตีที่ชื่อของอาร์เรย์เป็นแบบไดนามิก (เช่นเก็บไว้ในตัวแปร)


11

คำชี้แจงสั้น ๆ ของคำถาม:

มีวิธีการทุบตีในตัวเพื่อนับจำนวนขององค์ประกอบในอาร์เรย์ทุบตีที่ชื่อของอาร์เรย์เป็นแบบไดนามิก (เช่นเก็บไว้ในตัวแปร) โดยไม่ต้องหันไปทำสำเนาอย่างเต็มที่ของอาร์เรย์หรือใช้eval?

ข้อมูลมากกว่านี้:

การใช้การแทนที่พารามิเตอร์ bash สามารถทำได้ดังนี้:


  • myArr=(A B C); echo ${#myArr[@]}กำหนดความยาวของอาร์เรย์:
  • การอ้างอิงตัวแปรทางอ้อมโดยใช้ชื่อ:
    NAME=myVar; echo ${!NAME}
    (สิ่งนี้ยังใช้กับองค์ประกอบอาร์เรย์):
    NAME=myArr[1]; echo ${!NAME}

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

myArr=(A B C D)
NAME=myArr
# Get the number of elements in the array indirectly referenced by NAME.
count=${#$NAME[@]}  # This syntax is invalid. What is the right way?

ด้านล่างนี้เป็นความพยายามหลายครั้งที่ FAIL ทั้งหมด:

  # Setup for following attempts:
  myArr=(A B C D)
  NAME=myArr
  EXPR1=$NAME[@]          # i.e. EXPR1='myArr[@]'
  EXPR2=#$NAME[@]         # i.e. EXPR2='#myArr[@]'

  # Failed attempts to get the lengh of the array indirectly:
  1.  count=${#$NAME[@]}  # ERROR: bash: ...: bad substitution
  2.  count=${#!EXPR1}    # ERROR: bash: !EXPR}: event not found
  3.  count=${#\!EXPR1}   # ERROR: bash: ...: bad substitution
  4.  count=${!#EXPR1}    # ERROR: bash: ...: bad substitution
  5.  count=${!EXPR2}     # Returns NULL

ฉันได้พยายามยังมีบางสายพันธุ์อื่น ๆ ที่กล่าวมา แต่ยังไม่พบสิ่งใดที่ทำงานโดยทั้ง: (A) ทำสำเนาของอาร์เรย์หรือ (ข) evalโดยใช้

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

มีสองวิธีในการแก้ปัญหานี้ซึ่งอาจไม่เหมาะสม (แต่แก้ไขให้ฉันถ้าฉันผิด):

วิธีที่ 1: คัดลอกแถวลำดับ

กำหนดอาเรย์ให้กับตัวแปรอื่น (ตั้งชื่อแบบสแตติก) และรับจำนวนขององค์ประกอบในนั้น

EXPR=$NAME[@]
arrCopy=( "${!EXPR}" )
count=${#arrCopy}

วิธีที่ 2: ใช้ eval

EXPR="count=\${#$NAME[@]}"  # i.e. 'count=${myArr[@]}'
eval $EXPR
# Now count is set to the length of the array

สรุป:

มีวิธีการในตัว (เช่นการแทนที่พารามิเตอร์พารามิเตอร์) ในทุบตีเพื่อกำหนดความยาวของอาร์เรย์ทางอ้อม? ถ้าไม่วิธีที่มีประสิทธิภาพที่สุดในการทำเช่นนี้คืออะไร ฉันคิดว่ามันเป็นevalวิธีการข้างต้น แต่มีปัญหาด้านความปลอดภัยหรือประสิทธิภาพการทำงานด้วยevalหรือไม่


2
ฮึ. ตัวแปรที่ซ้อนกัน ฉันคิดใหม่ว่าวิธีใดที่ทำให้ฉันมาที่นี่ได้มากกว่าใช้ตัวแปรซ้อนกัน ปัญหาจริงที่นี่คืออะไร
muru

1
มันเป็นคำถามที่น่าสนใจ สิ่งเดียวที่ฉันจะเตือนคุณคือสมมติว่ามีหรือไม่มีปัญหาด้านประสิทธิภาพ ฉันพบว่าในระหว่างการทดสอบอย่างเข้มงวดเพื่อเพิ่มประสิทธิภาพสคริปต์ทุบตีที่มีขนาดใหญ่มากซึ่งตัวสร้างทุบตีบางตัวนั้นแย่มากในแง่ของประสิทธิภาพโดยการลบการทดสอบเริ่มต้นเพียงหนึ่งครั้งในสคริปต์ขนาดใหญ่ที่ใช้สิ่งที่คุณคาดหวังว่าจะมีประสิทธิภาพเช่น ในความเป็นจริงการขยายตัวของตัวแปรนั้นบรรทัดเดียวทำให้การประมวลผลทั้งหมดช้าลงประมาณ 10 ถึง 20% วิธีทดสอบในลูปขนาดใหญ่พร้อมตัวนับผลลัพธ์อาจทำให้คุณประหลาดใจ
Lizardx

2
bash namerefs? . declare -n ref=abc; abc=(A B C D); printf '%s\n' "${ref[@]}"
iruvar

@muru - นี่เป็นเพียงความหมาย แต่คำว่า "ตัวแปรที่ซ้อนกัน" เกี่ยวข้องกับการทุบตีก่อนรุ่นที่ 2 Bash v2 เพิ่มไวยากรณ์สำหรับ "การอ้างอิงตัวแปรทางอ้อม" ฉันแค่ถามว่ามีไวยากรณ์เฉพาะที่จะได้รับความยาวของอาร์เรย์อ้างอิงทางอ้อม ฉันคิดว่าผู้เขียน bash จะไม่พยายามใช้ความแปรปรวนของตัวแปรสำหรับ scalars และอาร์เรย์ถ้าไม่ใช่เทคนิคที่มีประโยชน์ที่ร้องขอ - ไม่ใช่แค่แฮ็คที่รับประกัน "Ugh" ทันทีแม้ว่าฉันจะแน่ใจว่ามันเป็นที่ถกเถียงกัน .
drwatsoncode

1
ฉันทำเกณฑ์มาตรฐานเล็กน้อย: time bash -c 'a=(1 a +); c=a; for ((i=0;i<100000;i++)); do eval "echo \${#$c[@]}"; done' > /dev/nullและคล้ายกับe=$c[@]; d=("${!e}); echo ${#d[@]}ในลูป Eval ใช้เวลาประมาณ 90% ในการคัดลอก และฉันคิดว่าช่องว่างนั้นจะเพิ่มเฉพาะอาเรย์และองค์ประกอบที่ใหญ่ขึ้นเท่านั้น
muru

คำตอบ:


4

คุณควรจัดการกับสิ่งนั้นในดัชนี evals และคุณสามารถอ้อมผ่านดัชนีตัวแปรทางอ้อมของคุณหากคุณทำให้มันเป็นอาร์เรย์

a=(abc1 def2 ghi3 jkl4 mno5)
r=('a[c=${#a[@]}]' a\[i] a\[@])
for   i in   0 1 2 3 4 5
do    c=
      printf "<%s>\n" "${!r-${!r[i<c?1:2]}}"
      printf "\n\tindex is $i and count is $c\n\n"
done

<abc1>

    index is 0 and count is 5

<def2>

    index is 1 and count is 5

<ghi3>

    index is 2 and count is 5

<jkl4>

    index is 3 and count is 5

<mno5>

    index is 4 and count is 5

<abc1>
<def2>
<ghi3>
<jkl4>
<mno5>

    index is 5 and count is 5

เนื่องจากbashดัชนีเป็นแบบอิง 0 จำนวนวัตถุทั้งหมดในอาร์เรย์จะทำงานมากกว่าดัชนีสูงสุดชุดหนึ่งเสมอและ:

c=
echo "${a[c=${#a[@]}]-this index is unset}" "$c"

this index is unset 5

... พารามิเตอร์จะขยายออกเป็นคำเริ่มต้นหากมีให้

หากไม่มีให้:

c=
${!r}
echo "$c"

5

... ไม่มีอันตรายใด ๆ เกิดขึ้น

ในลูปฉันติดตาม$iตัวแปร ndex และตรวจสอบว่าอย่างน้อยมีขนาดใหญ่เท่ากับ$count เมื่อมันน้อยลงฉันจะขยาย$reference var เป็นa[i]เพราะมันเป็นดัชนีที่ถูกต้อง แต่เมื่อมันเท่ากันหรือมากกว่าฉันจะขยาย$ref ไปยัง$array ทั้งหมด

นี่คือฟังก์ชั่น:

ref_arr(){
    local    index=-1 count=
    local    ref=(   "$1[ count= \${#$1[@]}  ]"
                     "$1[ index ]"    "$1[ @ ]"
    )  &&    printf  "input array '%s' has '%d' members.\n" \
                     "$1"  "${!ref-${count:?invalid array name: "'$1'"}}"
    while    [ "$((index+=1))" -lt "$count"  ]
    do       printf  "$1[$index]  ==  '%s'\n"  "${!ref[1]}"
    done
}
some_array=(some "dumb
            stuff" 12345\'67890 "" \
          '$(kill my computer)')
ref_arr some_array
ref_arr '$(echo won'\''t work)'

input array 'some_array' has '5' members.
some_array[0]  ==  'some'
some_array[1]  ==  'dumb
                stuff'
some_array[2]  ==  '12345'67890'
some_array[3]  ==  ''
some_array[4]  ==  '$(kill my computer)'
bash: count: invalid array name: '$(echo won't work)'

ขอให้เรายังคงอภิปรายนี้ในการแชท
drwatsoncode

0

ทุบตี 4.3 namerefs เป็นสวรรค์ อย่างไรก็ตามคุณสามารถทำได้:

$ myArr=(A B C D)
$ NAME=myArr
$ tmp="${NAME}[@]"
$ copy=( "${!tmp}" )
$ echo "${#copy[@]}"
4

ขอบคุณสำหรับการตอบกลับ แต่คำตอบของคุณคือสิ่งที่ฉันได้อธิบายไปแล้วในหัวข้อ "วิธีที่ 1: คัดลอกอาร์เรย์" คำถามนี้ยังระบุไว้โดยเฉพาะว่าควรกำหนดความยาวของอาร์เรย์ "โดยไม่ต้องหันไปทำสำเนาของอาร์เรย์" ซึ่งเป็นสิ่งที่คุณทำ
drwatsoncode
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.