Check 변수는 Bourne의 쉘과 같은 배열입니까?


14

배열 변수를 지원하는 쉘과 같은 Bourne에서는 파싱을 사용하여 변수가 배열인지 확인할 수 있습니다.

아래의 모든 명령은 실행 후 실행되었습니다 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

이 방법은 너무 많은 작업이므로 서브 쉘을 생성해야합니다. 내장 된 다른 쉘을 사용 하는 것은 서브 쉘을 필요 =~[[ ... ]]하지 않지만 여전히 너무 복잡합니다.

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


어떤 상황에서 변수가 배열인지 여부를 확인해야합니까?
Kusalananda

답변:


10

나는 당신이 할 수 있다고 생각하지 않으며, 그것이 실제로 차이를 만들지 않는다고 생각합니다.

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

x

그 중 하나에서 같은 일을 수행 ksh93bash. 아마도 것 같습니다 모든 변수는 특별한 속성을 할당되지 않은 그 껍질에 배열, 또는 적어도 정규 변수가 있지만 나는 그 정도의 확인하지 않았다.

bash매뉴얼에서는 배열을 사용할 때 배열과 문자열 변수에 대한 다른 동작에 대해 설명 +=하지만 나중에 헤지 (hedge)와 배열이 복합 지정 컨텍스트 에서만 다르게 동작한다고 설명합니다 .

또한 아래 첨자에 값이 할당 된 경우 변수가 배열로 간주되며 명시 적으로 null 문자열 가능성이 포함됩니다. 위에서 정규 할당이 확실히 아래 첨자가 할당됨을 알 수 있습니다. 따라서 모든 것이 배열이라고 생각합니다.

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

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

... 값 0의 단일 첨자에만 할당 된 설정 변수를 명확하게 찾아냅니다.


${a[1]-not array}작업을 수행 할 수 있는지 확인하는 것이 가능합니까?
cuonglm 2011

@cuonglm-음, bash매뉴얼 에 따르지 않음 : 첨자에 값이 할당되면 배열 변수가 설정된 것으로 간주됩니다. 널 문자열은 유효한 값입니다. 첨자에 사양별로 배열이 할당 된 경우. 실제로 할 수도 있기 때문에 아니요 a[5]=x. 나는 [ 1 -eq "${#a[@]}" ] && [ -n "${a[0]+1}" ]일할 수 있다고 생각한다 .
mikeserv

6

그래서 당신은 효과적으로 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

@mikeserv 좋은 지적입니다. 별명은 더 예쁘게 보이게합니다. +1
PSkocik

나는 alias vartype="$VARTYPE"-... 또는 전혀 정의하지 않았다는 것을 의미 $VARTYPE했습니다. 당신은 단지 그 필요로한다 shopt에 일을 bash그것과 관련된 사양으로 나누기 때문에 alias스크립트 확장.
mikeserv

1
@ mikeserv 나는 cuonglm이 그의 요구와 선호도에 대해이 접근법을 조정할 수 있다고 확신합니다. ;-)
PSkocik

보안 고려 사항
PSkocik

위의 코드 평가 사용자는 텍스트를 제공하지 않았습니다. 기능보다 안전하지 않습니다. 함수를 읽기 전용으로 만드는 것에 대한 소란을 본 적이 없지만 변수를 읽기 전용으로 표시 할 수 있습니다.
PSkocik

6

zsh에서

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

아마도 echo ${(t)var}더 간단 할 것입니다. 고마워

4

변수 var를 테스트하려면

b=("${!var[@]}")
c="${#b[@]}"

배열 인덱스가 두 개 이상 있는지 테스트 할 수 있습니다.

[[ $c > 1 ]] && echo "Var is an array"

첫 번째 색인 값이 0이 아닌 경우 :

[[ ${b[0]} -eq 0 ]] && echo "Var is an array"      ## should be 1 for zsh.

단 하나의 색인 값이 있고 해당 값이 0 (또는 1) 인 경우에 유일한 혼동이 있습니다.

해당 조건의 경우 배열이 아닌 변수에서 배열 요소를 제거하려고하면 부작용이 발생할 수 있습니다.

**bash** reports an error with             unset var[0]
bash: unset: var: not an array variable

**zsh** also reports an error with         $ var[1]=()
attempt to assign array value to non-array

이것은 bash에 올바르게 작동합니다.

# Test if the value at index 0 could be unset.
# If it fails, the variable is not an array.
( unset "var[0]" 2>/dev/null; ) && echo "var is an array."

zsh의 경우 인덱스는 1이어야합니다 (호환 모드가 활성화되어 있지 않은 경우).

하위 쉘은 var의 인덱스 0을 지울 때의 부작용을 피하기 위해 필요합니다.

ksh에서 작동시키는 방법을 찾지 못했습니다.

편집 1

이 함수는 bash4.2 이상에서만 작동합니다

getVarType(){
    varname=$1;
    case "$(typeset -p "$varname")" in
        "declare -a"*|"typeset -a"*)    echo array; ;;
        "declare -A"*|"typeset -A"*)    echo hash; ;;
        "declare -- "*|"typeset "$varname*| $varname=*) echo scalar; ;;
    esac;
}

var=( foo bar );  getVarType var

편집 2

이것은 bash4.2 이상에서만 작동합니다.

{ typeset -p var | grep -qP '(declare|typeset) -a'; } && echo "var is an array"

참고 : var에 테스트 된 문자열이 포함되어 있으면 위양성이 나타납니다.


원소가 0 인 배열은 어떻습니까?
cuonglm

1
Dat edit, tho. 매우 독창적입니다. : D
PSkocik

@cuonglm이 검사 ( unset "var[0]" 2>/dev/null; ) && echo "var is an array."는 var가 var=()요소가 0 인 배열 로 설정된 경우 var가 배열임을 올바르게보고 합니다. 선언과 정확히 동일하게 작동합니다.

스칼라를 내보내거나 정수 / 소문자 / 읽기 전용으로 표시하면 스칼라 테스트가 작동하지 않습니다. 비어 있지 않은 다른 출력이 스칼라 변수를 의미하도록 안전하게 만들 수 있습니다. GNU grep에 대한 의존성을 피하기 위해 grep -E대신에 사용하고 싶습니다 grep -P.
Stéphane Chazelas

@ StéphaneChazelas 정수 및 / 또는 소문자 및 / 또는 읽기 전용이있는 스칼라에 대한 테스트 (bash)는 항상 다음과 -a같이 시작 declare -airl var='()'합니다. 따라서 grep 테스트가 작동 합니다.

3

들어 bash는 , 그것은 (문서화이기는하지만) 해킹의 약간의 : 사용하려고은 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
}

typeset -g(bash-4.2 이상) 의 사용에 유의하십시오 .이 기능은 typeset(syn. declare)이 작동하지 않고 local검사하려는 값을 방해 하지 않도록 함수 내에서 필요합니다 . 이것은 함수 "가변"유형도 처리하지 않으며, typeset -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는 실제로 큰 차이가 없으며, (Changelog를 확인하면) 이것 중 일부는 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) 로 나타납니다.


typeset +a또한 KSH에서 오류를보고합니다. 그러나 zsh에는 없습니다.

1

짧은 답변:

이 표기법 ( bashksh93) 을 도입 한 두 쉘의 경우 스칼라 변수는 단일 요소를 갖는 배열입니다 .

배열 을 만들기 위해 특별한 선언이 필요하지 않습니다 . 과제만으로 충분하며 일반 과제는와 var=value동일합니다 var[0]=value.


시도하십시오 : bash -c 'unset var; var=foo; typeset -p var'. bash 응답이 배열을보고합니까 (-a 필요) ?. 이제 다음과 비교하십시오 bash -c 'unset var; var[12]=foo; typeset -p var'. 왜 차이점이 있습니까?. A : 쉘은 어떤 vars가 스칼라 또는 배열인지에 대한 개념을 유지합니다. 쉘 ksh는 두 개념을 하나로 혼합합니다.

1

yash의 array내장 에는 배열 변수에서만 작동하는 몇 가지 옵션이 있습니다. 예 : -d옵션은 비 배열 변수에 대한 오류를보고합니다.

$ a=123
$ array -d a
array: no such array $a

따라서 다음과 같이 할 수 있습니다.

is_array() (
  array -d -- "$1"
) >/dev/null 2>&1

a=(1 2 3)
if is_array a; then
  echo array
fi

b=123
if ! is_array b; then
  echo not array
fi

이 방법은 배열 변수가 읽기 전용 인 경우 작동하지 않습니다 . 읽기 전용 변수 를 수정하려고 하면 오류가 발생합니다.

$ a=()
$ readonly a
$ array -d a
array: $a is read-only

0
#!/bin/bash

var=BASH_SOURCE

[[ "$(declare -pa)" =~ [^[:alpha:]]$var= ]]

case "$?" in 
  0)
      echo "$var is an array variable"
      ;;
  1)
      echo "$var is not an array variable"
      ;;
  *)
      echo "Unknown exit code"
      ;;
esac
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.