배열 이름이 동적인(즉, 변수에 저장되는) bash 배열의 요소 수를 계산합니다.

배열 이름이 동적인(즉, 변수에 저장되는) bash 배열의 요소 수를 계산합니다.

문제에 대한 간략한 설명:

배열의 이름이 동적(예: 변수에 저장됨)인 bash 배열의 요소 수를 계산하는 내장 bash 메서드가 있습니까?아니요배열의 전체 복사본을 만들거나 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?

모두 실패한 여러 시도는 다음과 같습니다.

  # 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) 배열 복사본 만들기 또는 (B) 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

요약:

배열의 길이를 간접적으로 결정하기 위해 bash에 내장된 메서드(예: 매개변수 대체 구문)가 있습니까? 그렇지 않다면 가장 효율적인 방법은 무엇입니까? 나는 이것이 eval위의 접근 방식이라고 가정하지만 보안이나 성능 문제가 있습니까 eval?

답변1

인덱스 평가에서 이 항목을 처리해야 합니다. 간접적으로 할 수 있다통과하다배열로 만든 경우 간접변수의 인덱스입니다.

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

인덱싱은 0부터 시작 하므로 bash배열 객체의 총 개수는 항상 가장 높은 세트 인덱스보다 1이 더 많습니다. 따라서 다음과 같습니다.

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

this index is unset 5

...인수가 제공되면 인수가 기본 단어로 확장됩니다.

제공되지 않은 경우:

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

5

...아무런 피해도 입지 않았습니다.

루프에서 인덱스 변수를 추적 $i하고 그 변수가 적어도 $ccount만큼 큰지 확인합니다. 더 작을 때는 유효한 인덱스이기 때문에 ef를 확장 $r하지만 a[i], 그보다 크거나 같으면 $ref를 전체 $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)'

답변2

bash 4.3 nameref는 신의 선물입니다. 그러나 다음과 같이 할 수 있습니다.

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

관련 정보