bash 배열의 요소 수를 계산합니다. 여기서 배열의 이름은 동적입니다 (예 : 변수에 저장 됨)


11

질문에 대한 간략한 설명 :

이 내장되어 배시에있어서, 상기 어레이의 이름 (즉 변수에 저장) 동적 배시 배열의 요소 수를 계산하기 위해, 하지 A는 완전히 배열의 카피 제조 또는 사용에 의존 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있습니까?


2
어. 중첩 변수. 중첩 변수를 사용하는 것보다 여기에 도달 한 모든 접근법을 다시 생각할 것입니다. 실제 문제는 무엇입니까?
muru

1
흥미로운 질문입니다. 내가주의해야 할 유일한 것은 성능 문제가 있거나없는 것으로 가정하는 것입니다. 나는 매우 엄격한 테스트 동안 매우 큰 bash 스크립트를 최적화하기 위해 실제로 일부 bash 내장은 성능이 끔찍하다는 것을 알았습니다. 실제로는 큰 스크립트에서 하나의 시작 테스트를 제거하여 효율적으로 예상되는 것을 사용했습니다. 실제로 변수 확장으로 인해 단일 회선으로 인해 전체 실행 속도가 약 10 ~ 20 % 느려졌습니다. 타이머가있는 큰 루프의 메소드를 테스트하면 결과가 놀랍습니다.
Lizardx

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

@muru-이것은 의미 론적이지만 "중첩 변수"라는 용어는 버전 2 이전의 bash와 더 관련이 있습니다. Bash v2는 "간접 변수 참조"에 대한 구문을 추가했습니다. 간접적으로 참조 된 배열의 길이를 얻는 특정 구문이 있는지 묻고 있습니다. 나는 bash 제작자가 스칼라 배열이 요청되고 유용한 기술이 아니라면 스칼라 배열에 변수 간접 지시를 구현하려고 노력하지 않았을 것이라고 가정합니다. .
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 %가 걸렸습니다. 그리고 나는 간격이 배열과 그 요소가 더 커질 것이라고 생각합니다.
muru

답변:


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 만큼 큰지 확인합니다 . 더 적을 때 유효한 인덱스이기 때문에 $revar 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)'


0

bash 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.