별칭 있음

별칭 있음

배열 변수를 지원하는 Bourne과 같은 셸에서는 구문 분석을 사용하여 변수가 배열인지 확인할 수 있습니다.

아래의 모든 명령은 run 후에 실행됩니다 a=(1 2 3).

zsh:

$ declare -p a
typeset -a a
a=( 1 2 3 )

bash:

$ declare -p a
declare -a a='([0]="1" [1]="2" [2]="3")'

ksh93:

$ typeset -p a
typeset -a a=(1 2 3)

pdksh그리고 그 파생물:

$ typeset -p a
set -A a
typeset a[0]=1
typeset a[1]=2
typeset a[2]=3

yash:

$ typeset -p a
a=('1' '2' '3')
typeset a

다음의 예 bash:

if declare -p var 2>/dev/null | grep -q 'declare -a'; then
  echo array variable
fi

이 방법은 너무 많은 작업이 필요하며 서브셸을 생성해야 합니다. =~in 과 같은 다른 쉘 내장 기능을 사용하면 [[ ... ]]서브쉘이 필요하지 않지만 여전히 너무 복잡합니다.

이 작업을 수행하는 더 쉬운 방법이 있습니까?

답변1

나는 당신이 할 수 있다고 생각하지 않으며 실제로 아무런 차이가 없다고 생각합니다.

unset a
a=x
echo "${a[0]-not array}"

x

ksh93이는 및 에서 동일한 작업을 수행합니다 bash. 가능할 것 같다모두변수는 이러한 셸의 배열이거나 적어도 특별한 속성이 할당되지 않은 일반 변수이지만 많이 확인하지는 않았습니다.

매뉴얼 bash에서는 할당을 사용할 때 배열과 문자열 변수의 다양한 동작에 대해 설명 +=하지만 배열은 다음 상황에서만 다르게 동작한다는 것을 방지하고 명시합니다.화합물컨텍스트를 할당합니다.

또한 값이 첨자에 할당되면 변수가 배열로 처리되며 빈 문자열의 가능성이 명시적으로 포함되어 있음을 나타냅니다. 위에서 정규 할당으로 인해 반드시 첨자가 할당된다는 것을 알 수 있습니다. 따라서 모든 것이 배열인 것 같습니다.

실제로 다음을 사용할 수 있습니다.

[ 1 = "${a[0]+${#a[@]}}" ] && echo not array

...값이 0인 단일 첨자만 할당된 컬렉션 변수를 명확하게 찾아냅니다.

답변2

그렇다면 실제로 중간 부분만 원하고 declare -p주변 부분은 원하지 않는다는 건가요?

다음과 같은 매크로를 작성할 수 있습니다.

readonly VARTYPE='{ read __; 
       case "`declare -p "$__"`" in
            "declare -a"*) echo array;; 
            "declare -A"*) echo hash;; 
            "declare -- "*) echo scalar;; 
       esac; 
         } <<<'

이렇게 하면 다음을 수행할 수 있습니다.

a=scalar
b=( array ) 
declare -A c; c[hashKey]=hashValue;
######################################
eval "$VARTYPE" a #scalar
eval "$VARTYPE" b #array
eval "$VARTYPE" c #hash

(함수 로컬 변수에 사용하려는 경우 함수만으로는 작동하지 않습니다).


별칭 있음

shopt -s expand_aliases
alias vartype='eval "$VARTYPE"'

vartype a #scalar
vartype b #array
vartype c #hash

답변3

zsh에서

zsh% a=(1 2 3) s=1
zsh% [[ ${(t)a} == *array* ]] && echo array
array
zsh% [[ ${(t)s} == *array* ]] && echo array
zsh%

답변4

~을 위한세게 때리다, 이는 약간의 해킹입니다(문서화되어 있지만). 다음을 typeset사용하여 "배열" 속성을 제거해 보세요.

$ typeset +a BASH_VERSINFO
bash: typeset: BASH_VERSINFO: cannot destroy array variables in this way
echo $?
1

zsh( 배열을 스칼라로 변환할 수 있는 와 에서는 이 작업을 수행할 수 없습니다 . bash이 작업은 명시적으로 금지됩니다.)

그래서:

 typeset +A myvariable 2>/dev/null || echo is assoc-array
 typeset +a myvariable 2>/dev/null || echo is array

또는 함수의 끝에 있는 경고에 유의하세요.

function typeof() {
    local _myvar="$1"
    if ! typeset -p $_myvar 2>/dev/null ; then
        echo no-such
    elif ! typeset -g +A  $_myvar 2>/dev/null ; then
        echo is-assoc-array
    elif ! typeset -g +a  $_myvar 2>/dev/null; then
        echo is-array
    else
        echo scalar
    fi
}

(bash-4.2 이상) 의 사용에 유의하세요. 이는 (syn. ) 이 확인하려는 값처럼 동작하거나 손상되지 않도록 typeset -g함수에 필요합니다 . 이는 또한 함수 "변수" 유형을 처리하지 않으므로 필요한 경우 다른 분기 테스트를 추가할 수 있습니다.typesetdeclarelocaltypeset -f


또 다른 (거의 완전한) 옵션은 다음을 사용하는 것입니다.

    ${!name[*]}
          If name is an array variable, expands to  the  list
          of  array indices (keys) assigned in name.  If name
          is not an array, expands to 0 if name  is  set  and
          null  otherwise.   When @ is used and the expansion
          appears within double quotes, each key expands to a
          separate word.

그러나 작은 문제가 있습니다. 인덱스가 0인 단일 배열은 위의 두 가지 조건을 충족합니다. mikeserv도 이것을 인용했는데, bash에는 실제로 엄격한 차이가 없습니다. 그 중 일부(변경 로그를 확인하면)는 ksh와 ${name[*]}비배열에서 작동하는 방식과의 호환성 때문일 수 있습니다.${name[@]}

그래서부분의해결책은 다음과 같습니다.

if [[ ${!BASH_VERSINFO[*]} == '' ]]; then
    echo no-such
elif [[ ${!BASH_VERSINFO[*]} == '0' ]]; then 
    echo not-array
elif [[ ${!BASH_VERSINFO[*]} != '0' ]]; 
    echo is-array    
fi

나는 과거에 이 방법의 변형을 사용해 왔습니다.

while read _line; do
   if [[ $_line =~ ^"declare -a" ]]; then 
     ...
   fi 
done < <( declare -p )

그러나 이를 위해서는 서브셸도 필요합니다.

유용할 수 있는 또 다른 기술은 다음과 같습니다 compgen.

compgen -A arrayvar

이것은 모든 인덱스 배열을 나열하지만 연관 배열은 (bash-4.4까지) 특별하게 처리되지 않고 일반 변수( compgen -A variable) 로 표시됩니다.

관련 정보