বাশ অ্যারেতে উপাদানগুলির সংখ্যা গণনা করুন, যেখানে অ্যারের নামটি গতিশীল (যেমন একটি ভেরিয়েবলে সঞ্চিত)


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% কমিয়ে দেয়। টাইমার সহ বড় লুপগুলিতে পরীক্ষার পদ্ধতিগুলি, ফলাফলগুলি আপনাকে অবাক করে দিতে পারে।
Lizardx

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

@ মুরু - এটি কেবল শব্দার্থক, তবে "নেস্টেড ভেরিয়েবল" শব্দটি সংস্করণ ২-এর আগে বাশের সাথে আরও বেশি সম্পর্কিত tes আমি কেবল জিজ্ঞাসা করছি যে কোনও অপ্রত্যক্ষভাবে রেফারেন্স করা অ্যারের দৈর্ঘ্য পেতে একটি নির্দিষ্ট বাক্য গঠন আছে কিনা। আমি ধরে নিলাম বাশ লেখকরা স্কেলার এবং অ্যারেগুলির জন্য ভেরিয়েবল ইন্ডিরিয়ারেশন বাস্তবায়নের প্রয়াসে যেতে পারতেন না যদি এটি কোনও অনুরোধকৃত, দরকারী কৌশল না হয় - কেবল একটি হ্যাক না যা তাত্ক্ষণিকভাবে "উঘ" এর নিশ্চয়তা দেয়, যদিও আমি নিশ্চিত যে এটি বিতর্কযোগ্য ।
ড্রওয়াটসনকোড

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

... কোনও ক্ষতি হয় নি।

$iলুপটিতে আমি একটি এনডিএক্স ভেরিয়েবল ট্র্যাক করি এবং এটি কমপক্ষে $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

bash 4.3 নেমরেফ একটি গডসেন্ড। তবে, আপনি এটি করতে পারেন:

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

জবাব দেওয়ার জন্য ধন্যবাদ, তবে আপনার উত্তরটি আমি ইতিমধ্যে "পদ্ধতি 1: অ্যারের অনুলিপি" বিভাগের অধীনে বর্ণনা করেছি। প্রশ্নটিও সুনির্দিষ্টভাবে জানিয়েছে যে অ্যারের দৈর্ঘ্যটি "অ্যারের সম্পূর্ণ অনুলিপি তৈরি না করে" নির্ধারণ করা উচিত, যা আপনি ঠিক তাই করেছিলেন।
ড্রওয়াটসনকোড
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.