쉘에 의한 배열 지원 테스트


12

명령 줄에서 로컬 Bourne과 같은 쉘에서 어레이 지원을 간결하게 테스트하는 방법이 있습니까?

이것은 항상 가능합니다 :

$ arr=(0 1 2 3);if [ "${arr[2]}" != 2 ];then echo "No array support";fi

또는 $SHELL쉘 버전 테스트 및 테스트 :

$ eval $(echo "$SHELL --version") | grep version

그런 다음 액세스 권한이 있다고 가정하고 매뉴얼 페이지를 읽습니다. (심지어 거기에서 편지를 쓰고 /bin/bash, 나는 가정하고 모든 본쉘 긴 옵션을 인정 --version, KSH에 대한 그 휴식 예를 들어 .)

스크립트 시작시 또는 호출 전에 사용 섹션에 무인으로 통합 할 수있는 간단한 테스트를 찾고 있습니다.


Bourne과 같은 껍질로 제한하고 싶다고 생각합니까?
Stéphane Chazelas

@ StéphaneChazelas : 예, sh, csh, ksh, tcsh, bash, zsh 및 친한 친구로 구성된 핵심 그룹을 의미하는 것은 아닙니다. 나는이 별자리에서 yash가 어디에 위치하는지 모른다.
Cbhihe

2
csh본 쉘이 아닙니다. tcsh하나도 아닙니다 ( csh일부 버그가 수정되었습니다)
cas

1
참고 $SHELL사용자의 선호 셸은 같은 $EDITOR그가 선호하는 텍스트 편집기입니다. 현재 실행중인 쉘과 거의 관련이 없습니다.
Stéphane Chazelas

1
eval$SHELL --version쉘 코드로 출력을 사용 하는 것은 의미가 없습니다.
Stéphane Chazelas

답변:


12

당신은 본쉘 (다른 많은 조개 등을 제한 할 가정 csh, tcsh, rc, es또는 fish지원 배열하지만 껍질 같은 Bourne 씨에 동시에 호환 스크립트를 작성하고 완전히 다른 및 통역과 마찬가지로 사람들은 까다 롭고 일반적으로 무의미 호환되지 않는 언어), 구현 간에는 상당한 차이가 있습니다.

배열을 지원하는 Bourne과 같은 쉘은 다음과 같습니다.

  • ksh88(배열을 구현하는 최초의 하나가, ksh88 여전히 같은 발견 ksh은 또한의 기초가 어디 대부분 기존의 상용 유닉스에서 sh)

    • 배열은 1 차원입니다
    • 배열로 정의되어 있습니다 set -A array foo bar또는 set -A array -- "$var" ...당신은 보장 할 수없는 경우 그 $var이 시작되지 않습니다 -또는 +.
    • 배열 인덱스는에서 시작 0합니다.
    • 개별 배열 요소는로 지정됩니다 a[1]=value.
    • 배열이 드물다. 즉 a[5]=foo경우에도 작동 a[0,1,2,3,4]설정되지 않은 및 해제를 떠날 것이다.
    • ${a[5]}indice 5의 요소에 액세스하기 위해 (배열이 드문 경우 반드시 여섯 번째 요소는 아닙니다). 는 5산술 표현식이있을 수있다.
    • 배열 크기와 아래 첨자는 4096으로 제한됩니다.
    • ${#a[@]} 배열에서 할당 된 요소의 수입니다 (가장 큰 할당 된 지표는 아님).
    • 지정된 첨자 목록을 알 수있는 방법은 없습니다 (4096 개 요소를 개별적으로 테스트하는 것 제외 [[ -n "${a[i]+set}" ]]).
    • $a와 동일합니다 ${a[0]}. 즉, 배열은 스칼라 변수에 추가 값을 제공하여 확장합니다.
  • pdksh그리고 파생 상품 (이것은 ksh때로는 sh여러 BSD 의 기초이며 ksh93 소스가 해제되기 전에 유일한 오픈 소스 ksh 구현이었습니다) :

    주로 좋아 ksh88하지만 참고하십시오 :

    • 일부 오래된 구현은을 지원하지 않았습니다 set -A array -- foo bar( --필요하지 않았습니다).
    • ${#a[@]}가장 큰 배정 된 지표의 지수에 1을 더한 것입니다. ( a[1000]=1; echo "${#a[@]}"배열에 요소가 하나만 있어도 1001을 출력합니다.
    • 최신 버전에서는 배열 크기가 더 이상 제한되지 않습니다 (정수 크기 제외).
    • 최근 버전 mksh에서 영감을 몇 가지 추가 연산자를 가지고 bash, ksh93또는 zsh라 과제처럼 a=(x y), a+=(z), ${!a[@]}할당 된 인덱스의 목록을 얻을 수 있습니다.
  • zsh. zsh배열은 일반적으로 더 나은 디자인과 최고의 가지고 있습니다 kshcsh배열을. 그것들은 비슷 ksh하지만 큰 차이점이 있습니다.

    • 인덱스는 0이 아닌 1에서 시작합니다 ( ksh에뮬레이션 제외 ). Bourne 배열 ( zsh$ argv 배열로 표시 되는 위치 매개 변수 $ @ ) 및 csh배열 과 일치 합니다.
    • 그것들은 정규 / 스칼라 변수와는 별개의 유형입니다. 운영자는 일반적으로 기대하는 것처럼 다르게 적용됩니다. $a는 같지 ${a[0]}않지만 배열의 비어 있지 않은 요소로 확장됩니다 ( "${a[@]}"에서와 같은 모든 요소에 대해 ksh).
    • 그것들은 희소 배열이 아닌 일반 배열입니다. a[5]=1작동하지만 1에서 4까지의 모든 요소가 할당되지 않은 경우 빈 문자열을 할당합니다. 따라서 ${#a[@]}( ${#a}ksh에서 indice 0의 요소 크기 와 동일 )는 배열의 요소 수 가장 큰 할당 된 indice입니다.
    • 연관 배열이 지원됩니다.
    • 배열로 작업하기위한 수많은 연산자가 지원되며 여기에 나열하기에는 너무 큽니다.
    • 로 정의 된 배열 a=(x y). set -A a x y또한 작동하지만 set -A a -- x yksh 에뮬레이션이 아닌 경우 지원되지 않습니다 ( --zsh 에뮬레이션에는 필요하지 않음).
  • ksh93. (여기서는 최신 버전을 설명합니다). FOSS로 출시 된 지금은 ksh93오랫동안 실험으로 간주 된 실험 이 점점 더 많은 시스템에서 발견 될 수 있습니다. 예를 들어, /bin/sh(Bourne 쉘을 대체 /usr/xpg4/bin/sh한 POSIX 쉘은 여전히을 기반으로합니다 ksh88) 및 kshSolaris 11입니다. 그 배열은 ksh88을 확장하고 향상시킵니다.

    • a=(x y)는 배열을 정의하는 데 사용될 수 있지만 a=(...)복합 변수 ( a=(foo=bar bar=baz)) 를 정의하는 데에도 사용 되므로 a=()모호하며 배열이 아닌 복합 변수를 선언합니다.
    • 배열은 다차원 ( a=((0 1) (0 2)))이며 배열 요소는 복합 변수 ( a=((a b) (c=d d=f)); echo "${a[1].c}") 일 수도 있습니다 .
    • a=([2]=foo [5]=bar)구문은 한 번에 스파 스 배열을 정의 할 수 있습니다.
    • 크기 제한이 해제되었습니다.
    • 의 범위는 zsh아니지만 배열을 조작하는 데 지원되는 많은 연산자가 있습니다.
    • "${!a[@]}" 배열 인덱스 목록을 검색합니다.
    • 연관 배열도 별도의 유형으로 지원됩니다.
  • bash. bashGNU 프로젝트의 쉘입니다. 그것은으로 사용되는 shOS / X 및 일부 GNU / Linux 배포판의 최신 버전에. bash배열은 주로 및의 ksh88일부 기능이있는 배열을 에뮬레이션 합니다 .ksh93zsh

    • a=(x y)지원됩니다. 지원 set -A a x y 되지 않습니다 . a=()빈 배열을 만듭니다 (에 복합 변수 없음 bash).
    • "${!a[@]}" 지수 목록.
    • a=([foo]=bar)ksh93및 에서 제공되는 구문뿐만 아니라 다른 구문도 지원합니다 zsh.
    • 최신 bash버전은 또한 연관 배열을 별도의 유형으로 지원합니다.
  • yash. 비교적 최신의 깨끗한 멀티 바이트 인식 POSIX sh 구현입니다. 널리 사용되지 않습니다. 배열은 다음과 비슷한 또 다른 깨끗한 API입니다.zsh

    • 배열이 희박하지 않습니다
    • 배열 인덱스는 1에서 시작
    • 로 정의 및 선언 a=(var value)
    • array내장으로 삽입, 삭제 또는 수정 된 요소
    • array -s a 5 value해당 요소가 사전에 지정되지 않은 경우 5 번째 요소 를 수정하면 실패합니다.
    • 배열의 요소 수는 ${a[#]}, ${#a[@]}리스트와 요소의 크기 인.
    • 배열은 별도의 유형입니다. a=("$a")요소를 추가하거나 수정하기 전에 스칼라 변수를 배열로 재정의 해야 합니다.
    • 로 호출되면 배열이 지원되지 않습니다 sh.

따라서 배열 지원을 감지하는 것을 볼 수 있습니다.

if (unset a; set -A a a; eval "a=(a b)"; eval '[ -n "${a[1]}" ]'
   ) > /dev/null 2>&1
then
  array_supported=true
else
  array_supported=false
fi

그 배열을 사용할 수있을만큼 충분하지 않습니다. 배열을 전체 및 개별 요소로 할당하기 위해 래퍼 명령을 정의하고 희소 배열을 만들지 않도록해야합니다.

처럼

unset a
array_elements() { eval "REPLY=\"\${#$1[@]}\""; }
if (set -A a -- a) 2> /dev/null; then
  set -A a -- a b
  case ${a[0]}${a[1]} in
    --) set_array() { eval "shift; set -A $1"' "$@"'; }
        set_array_element() { eval "$1[1+(\$2)]=\$3"; }
        first_indice=0;;
     a) set_array() { eval "shift; set -A $1"' -- "$@"'; }
        set_array_element() { eval "$1[1+(\$2)]=\$3"; }
        first_indice=1;;
   --a) set_array() { eval "shift; set -A $1"' "$@"'; }
        set_array_element() { eval "$1[\$2]=\$3"; }
        first_indice=0;;
    ab) set_array() { eval "shift; set -A $1"' -- "$@"'; }
        set_array_element() { eval "$1[\$2]=\$3"; }
        first_indice=0;;
  esac
elif (eval 'a[5]=x') 2> /dev/null; then
  set_array() { eval "shift; $1=("'"$@")'; }
  set_array_element() { eval "$1[\$2]=\$3"; }
  first_indice=0
elif (eval 'a=(x) && array -s a 1 y && [ "${a[1]}" = y ]') 2> /dev/null; then
  set_array() { eval "shift; $1=("'"$@")'; }
  set_array_element() {
    eval "
      $1=(\${$1+\"\${$1[@]}"'"})
      while [ "$(($2))" -ge  "${'"$1"'[#]}" ]; do
        array -i "$1" "$2" ""
      done'
    array -s -- "$1" "$((1+$2))" "$3"
   }
  array_elements() { eval "REPLY=\${$1[#]}"; }
  first_indice=1
else
  echo >&2 "Array not supported"
fi

그리고 당신은 배열을 가진 요소에 액세스 "${a[$first_indice+n]}"하여, 전체 목록 "${a[@]}"과 래퍼 함수를 사용 ( array_elements, set_array, set_array_element) (에서 배열의 요소 수를 얻기 위해 $REPLY), 전체 또는 할당 개별 요소로 배열을 설정합니다.

노력할만한 가치가 없을 것입니다. perlBourne / POSIX 셸 배열을 사용 하거나 제한합니다 "$@".

내부적으로 배열을 사용하는 함수를 정의하기 위해 사용자의 대화식 쉘에서 일부 파일을 소싱하려는 의도가 있다면 유용한 몇 가지 참고 사항이 있습니다.

로컬 범위 (함수 또는 익명 함수)의 배열 zsh과 유사하게 배열을 구성 할 수 있습니다 ksh.

myfunction() {
  [ -z "$ZSH_VERSION" ] || setopt localoption ksharrays
  # use arrays of indice 0 in this function
}

또한 다음을 사용하여 에뮬레이션 할 수 있습니다 ksh( ksh배열 및 기타 여러 영역과의 호환성 향상 ).

myfunction() {
  [ -z "$ZSH_VERSION" ] || emulate -L ksh
  # ksh code more likely to work here
}

이를 염두에두고 파생 버전의 이전 버전과 이전 버전에 대한 지원을 기꺼이 포기 yash하고 희소 배열을 만들지 않는 한 일관성있게 사용할 수 있어야합니다.ksh88pdksh

  • a[0]=foo
  • a=(foo bar)(하지만 아닙니다 a=())
  • "${a[#]}", "${a[@]}","${a[0]}"

이 이러한 기능에 emulate -L ksh그동안, zsh사용자가 정상적으로 그 / 그녀의 배열 zsh을 방법을 사용하여.


7

eval배열 구문을 시도하는 데 사용할 수 있습니다 .

is_array_support() (
  eval 'a=(1)'
) >/dev/null 2>&1

if is_array_support; then
  echo support
else
  echo not
fi

2
ksh88배열은 지원하지만 지원하지는 않습니다 a=(). 에서 ksh93, a=()화합물 변수 아닌 가변 미리 배열로 선언되지 않는 배열을 선언한다.
Stéphane Chazelas

2
또한 배열 구현 간에는 상당한 차이가 있습니다. 예를 들어, 일부는 0에서 시작하는 배열 색인 (bash, ksh, ksh 에뮬레이션의 zsh)을 갖고 일부는 1에서 시작합니다 (zsh, yash). 일부는 일반 배열 / 목록이고, 일부는 드문 배열입니다 (ksh 또는 bash에서와 같이 양의 정수로 제한되는 키가있는 연관 배열).
Stéphane Chazelas

에서 yash, 당신은 a[5]=1하지만array -s a 5 1
Stéphane Chazelas

@ StéphaneChazelas : 정확성에 감사드립니다. 필자의 경우 모든 것이 배열 (연관 여부)을 지원하는지 여부에 달려 있습니다. 무인으로 실행되는 스크립트에서도 인덱스 기반에 대한 세부 사항을 쉽게 해결할 수 있습니다.
Cbhihe

@ StéphaneChazelas : 복합 변수가 ksh93놀랍도록 만들었습니다. 관련 문서의 일부를 제공해 주시겠습니까? 1배열에 추가 하여 작동시킵니다.
cuonglm
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.