बैश सरणी में तत्वों की संख्या की गणना करें, जहां सरणी का नाम गतिशील है (यानी एक चर में संग्रहीत)


11

प्रश्न का संक्षिप्त विवरण:

क्या बाश सरणी में तत्वों की संख्या की गणना करने के लिए अंतर्निहित बैश विधि है, जहां सरणी का नाम डायनामिक है (अर्थात एक चर में संग्रहीत), बिना सरणी की पूरी तरह से प्रतिलिपि बनाने या उपयोग करने के लिए eval?

अधिक जानकारी:

बैश पैरामीटर प्रतिस्थापन का उपयोग करना, एक निम्नलिखित कर सकता है:

  • एक सरणी की लंबाई निर्धारित करें
    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?

नीचे कई प्रयास हैं जो सभी विफल हैं:

  # 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

मैंने ऊपर के कुछ अन्य वेरिएंट भी आज़माए हैं, लेकिन अभी तक ऐसा कुछ भी नहीं पाया है जो या तो बिना काम करे: (ए) का उपयोग करके सरणी (बी) की एक प्रति बना रहा हो 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
ओह। नेस्टेड चर। मैं नेस्टेड वैरिएबल की तुलना में जो भी एप्रोच मुझे यहां मिली थी, उस पर फिर से विचार करूंगा। यहां वास्तविक समस्या क्या है?
मुरु

1
यह एक दिलचस्प सवाल है। केवल एक चीज मैं आपको सावधान कर दूंगा कि आप यह मान रहे हैं कि कोई प्रदर्शन समस्या है या नहीं है। मैंने बहुत बड़े बैश स्क्रिप्ट को अनुकूलित करने के लिए बहुत कठोर परीक्षण के दौरान पाया कि कुछ बैश बिल्डिंग्स प्रदर्शन के मामले में भयानक थे, वास्तव में, एक बड़ी स्क्रिप्ट में एक स्टार्ट अप टेस्ट को हटाकर, इसका इस्तेमाल आप कुशल होने की उम्मीद कर सकते थे, अर्थात , चर विस्तार, वास्तव में, उस एकल लाइन ने पूरे निष्पादन को लगभग 10 से 20% तक धीमा कर दिया। टाइमर के साथ बड़े छोरों में परीक्षण के तरीके, परिणाम आपको आश्चर्यचकित कर सकते हैं।
छिपकली

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

@ एमुरु - यह सिर्फ शब्दार्थ है, लेकिन शब्द "नेस्टेड चर" संस्करण 2 से पहले बैश करने के लिए अधिक संबंधित है। बैश v2 ने "अप्रत्यक्ष चर संदर्भ" के लिए एक वाक्यविन्यास जोड़ा। मैं सिर्फ यह पूछ रहा हूं कि अप्रत्यक्ष रूप से संदर्भित सरणी की लंबाई प्राप्त करने के लिए एक विशिष्ट वाक्यविन्यास है या नहीं। मैं bash लेखकों scalars के लिए चर अविवेक को लागू करने के प्रयास करने के लिए चले गए हैं नहीं होगा मान और , ना कि केवल एक हैक warranting एक तत्काल "ओह" हालांकि मुझे यकीन है कि यह बहस का मुद्दा है हूँ - सरणियों अगर यह एक अनुरोध किया है, उपयोगी तकनीक नहीं थे ।
drwatsoncode 5

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[@]}लूप में। निष्कासन को नकल करने में लगभग 90% समय लगा। और मुझे लगता है कि अंतर केवल बड़े सरणी और उसके तत्वों को बढ़ाएगा।
मूरू

जवाबों:


4

आपको उस सामान को इंडेक्स में लाना चाहिए। और यदि आप इसे एक सरणी बनाते हैं तो आप अपने अप्रत्यक्ष चर के सूचकांकों के माध्यम से अप्रत्यक्ष कर सकते हैं ।

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

... कोई नुकसान नहीं हुआ है।

लूप में मैं एक $index वैरिएबल को ट्रैक करता हूं और जांचता हूं कि यह कम से कम $count जितना बड़ा है । जब यह कम होता है तो मैं $rइरेक्शन संस्करण का विस्तार करता हूं a[i]क्योंकि यह एक वैध सूचकांक है, लेकिन जब यह बराबर या अधिक होता है तो मैं $rपूरे $aऐरे में एफईएफ का विस्तार करता हूं।

यहाँ यह एक समारोह में है:

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)'


0

बैश 4.3 नामेरेफ़्स एक देवता हैं। हालाँकि, आप ऐसा कर सकते हैं:

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

उत्तर देने के लिए धन्यवाद, लेकिन आपका जवाब वही है जो मैंने पहले से ही अनुभाग "विधि 1: कॉपी द एरे" के तहत वर्णित किया है। यह प्रश्न भी विशेष रूप से कहा गया है कि सरणी की लंबाई "सरणी की पूरी तरह से नकल करने का सहारा लिए बिना" निर्धारित की जानी चाहिए, जो कि आपने ठीक किया।
drwatsoncode
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.