Check 변수는 Bourne의 쉘과 같은 배열입니까? 쉘과 같은 Bourne에서는 파싱을 사용하여 변수가 배열인지

배열 변수를 지원하는 쉘과 같은 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

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

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



답변

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

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

x

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

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

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

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

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

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


답변

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


답변

zsh에서

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


답변

변수 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에 테스트 된 문자열이 포함되어 있으면 위양성이 나타납니다.


답변

들어 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) 로 나타납니다.


답변

짧은 답변:

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

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


답변

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