Unix Bourne Shell의 배열


26

Bourne shell ( /bin/sh) 에서 배열을 사용하려고합니다 . 배열 요소를 초기화하는 방법은 다음과 같습니다.

arr=(1 2 3)

그러나 오류가 발생했습니다.

syntax error at line 8: `arr=' unexpected

이제이 구문을 찾은 게시물은에 bash대한 것인데 Bourne 쉘에 대한 별도의 구문을 찾을 수 없습니다. 구문도 동일 /bin/sh하게 적용됩니까?


1
이 질문을 확인하십시오 stackoverflow.com/questions/9481702/… 스택 오버플로
Nischay

1
Thnx @Nischay은 ... 당신은 제공된 링크를 읽은 후, 구글에서 내 쿼리 문자열을 정제하고 링크 있어요 - docstore.mik.ua/orelly/unix/upt/ch45_34.htm
SubhasisM

답변:


47

/bin/sh요즘은 모든 시스템에서 Bourne 쉘이 될 수는 없습니다 (Solaris 11에서 / bin / sh를 포함하여 마지막으로 주요 시스템 중 하나였던 Solaris조차도). /bin/sh70 년대 초의 톰슨 껍질이었습니다. Bourne 쉘은 1979 년 Unix V7에서이를 대체했습니다.

/bin/sh 그 후 수년 동안 Bourne 쉘 (또는 Almquist 쉘, BSD에서 무료로 다시 구현)이었습니다.

요즘 /bin/sh은 POSIX sh언어 의 통역사 또는 다른 언어로, ksh88 언어의 하위 세트 (및 일부 비 호환성을 가진 Bourne 쉘 언어의 상위 세트)를 기반으로합니다.

Bourne 쉘 또는 POSIX sh 언어 사양은 배열을 지원하지 않습니다. 또는 오히려 그들은 단지 하나 개의 배열을 가지고 : 위치 매개 변수 ( $1, $2, $@, 기능마다 하나 개의 배열을 잘 참조).

ksh88에는으로 설정 한 배열이 set -A있지만 구문이 까다 롭고 사용할 수 없으므로 POSIX sh에 지정되지 않았습니다.

어레이 /리스트 변수와 다른 쉘을 포함한다 : csh/ tcsh, rc, es, bash(주로 KSH 구문에게 ksh93의 방법을 복사 함), yash, zsh, fish다른 구문 (각 rc유닉스하려면-일 후에 후속의 쉘 fishzsh가장 부합되는 그들) ...

기본적으로 sh(Bourne 쉘의 최신 버전에서도 작동) :

set '1st element' 2 3 # setting the array

set -- "$@" more # adding elements to the end of the array

shift 2 # removing elements (here 2) from the beginning of the array

printf '<%s>\n' "$@" # passing all the elements of the $@ array 
                     # as arguments to a command

for i do # looping over the  elements of the $@ array ($1, $2...)
  printf 'Looping over "%s"\n' "$i"
done

printf '%s\n' "$1" # accessing individual element of the array.
                   # up to the 9th only with the Bourne shell though
                   # (only the Bourne shell), and note that you need
                   # the braces (as in "${10}") past the 9th in other
                   # shells.

printf '%s\n' "$# elements in the array"

printf '%s\n' "$*" # join the elements of the array with the 
                   # first character (byte in some implementations)
                   # of $IFS (not in the Bourne shell where it's on
                   # space instead regardless of the value of $IFS)

(Bourne 쉘 및 ksh88에,주의 $IFS의 공백 문자 메시지 있어야 "$@"일을 제대로 (버그) 및 Bourne 쉘에서 수행 할 수 있습니다 위의 액세스 할 요소 $9( ${10}하지 작업, 당신은 아직 할 수있는 것 shift 1; echo "$9"이상 또는 루프 그들)).


2
고마워요 ... 자세한 설명은 매우 도움이되었습니다.
SubhasisM

1
일부 주요 기능에서 위치 매개 변수가 bash 배열과 다름을 주목할 가치가 있습니다. 예를 들어, 희소 배열을 지원하지 않으며 sh에는 슬라이싱 매개 변수 확장이 없으므로와 같은 하위 목록에 액세스 할 수 없습니다 "${@:2:4}". 확실히, 나는 유사점 을 보지만 위치 매개 변수는 배열 자체 로 간주하지 않습니다 .
kojiro

@kojiro, 어느 정도, 나는 반대로 말하고 싶지만 "$@"배열 같은 역할 (의 배열과 같은 csh, rc, zsh, fish, yash, 그것은 더 ...)을 콘 / bash는 정말 배열 아니다 "배열"하지만 일부 키가 양의 정수로 제한되는 연관 배열 형식 (배열 및 "$ @"이있는 다른 모든 쉘에서와 같이 1 대신 0부터 시작하는 인덱스도 있음). 슬라이싱을 지원하는 쉘은 $ @를 똑같이 슬라이스 할 수 있습니다 ( "$ @"를 슬라이스 할 때 ksh93 / bash가 위치 매개 변수에 $ 0를 어색하게 추가 함).
Stéphane Chazelas

3

일반 Bourne 셸에는 배열이 없습니다. 다음 방법을 사용하여 배열을 만들고 탐색 할 수 있습니다.

#!/bin/sh
# ARRAY.sh: example usage of arrays in Bourne Shell

array_traverse()
{
    for i in $(seq 1 $2)
    do
    current_value=$1$i
    echo $(eval echo \$$current_value)
    done
    return 1
}

ARRAY_1=one
ARRAY_2=two
ARRAY_3=333
array_traverse ARRAY_ 3

배열을 사용하는 방법에 관계없이 sh항상 번거 롭습니다. 같은 다른 언어를 사용하는 것을 고려 Python하거나 Perl당신은 매우 제한된 플랫폼과 붙어 또는 뭔가를 배울하려는 경우가 아니면 할 수 있습니다.


답변 주셔서 감사합니다 ... !! 실제로 나는 실제로 쉘 스크립트에서 물건을 배우려고 노력하고 있습니다 ... 그렇지 않으면 파이썬에서 배열을 구현하는 것은 실제로 케이크 조각입니다. 이것은 배열을 지원하지 않는 스크립팅 언어가 있다는 큰 교훈이었습니다. 한 가지, 게시 한 코드에 오류가 있습니다. 지금, 나는 그것을 해결할 것입니다 ... plz 귀찮게하지 마십시오.
SubhasisM

@NoobGeek, Bourne 쉘에는 $(...)구문 이 없습니다 . 따라서 실제로 Bourne 쉘이 있어야합니다. Solaris 10 또는 그 이전에 있습니까? 기회가 없을 수도 seq있습니다. Solaris 10 및 이전 버전에서는 / usr / xpg4 / bin / sh가 shBourne 쉘 대신 표준을 갖기를 원합니다 . seq그 방법을 사용 하는 것도 좋지 않습니다.
Stéphane Chazelas

POSIX는 명령 대체에서 link 와 $가 동일하다고 명시합니다 . 그리고 왜 seq그런 식으로 사용 하는 것이 좋지 않습니까?
Arkadiusz Drabczyk

2
예 POSIX 쉘에서, 당신은 선호합니다 $(...)이상 `하지만, 영업 이익의는 /bin/sh아마 Bourne 쉘이 아닌 POSIX 쉘입니다. 옆 seq하 표준 명령이없는 $(seq 100)메모리의 전체 출력을 저장하는 수단, 및 그 수단은 개행 함유 $ IFS의 현재 값에 의존 및 숫자를 포함하지. 사용 i=1; while [ "$i" -le 100 ]; ...; i=$(($i + 1)); done하는 것이 가장 좋습니다 (Bourne 쉘에서도 작동하지 않지만).
Stéphane Chazelas

1
@Daenyth 나는 반대로 bashisms을 배우고 /bin/sh나중에 이식 가능한 구문 을 배우면 사람들이 잘못된 #!/bin/shshebang 을 사용하는 것이 좋다고 생각하게하고 다른 사람들이 그것을 사용하려고 할 때 스크립트를 깨뜨리는 경향이 있습니다. 이런 종류의 화염 미끼를 게시하지 않는 것이 좋습니다. :)
Josip Rodin

2

다른 사람들이 말했듯이, Bourne Shell에는 실제 배열 이 없습니다 .

그러나 필요한 작업에 따라 구분 된 문자열로 충분합니다.

sentence="I don't need arrays because I can use delimited strings"
for word in $sentence
do
  printf '%s\n' "$word"
done

일반적인 구분 기호 (공백, 탭 및 줄 바꿈)로 충분하지 않은 IFS경우 루프 전에 원하는 구분 기호로 설정할 수 있습니다 .

프로그래밍 방식으로 배열을 작성해야하는 경우 구분 된 문자열을 작성하면됩니다.


1
당신이 그것을 원치 않는 한 (아마도), 당신은 아마도 그와 같은 변수를 인용하지 않은 채로 놓는 또 다른 효과 ( split+glob연산자) 인 globbing을 비활성화하고 싶을 것입니다 .
Stéphane Chazelas

0

배열을 대시로 시뮬레이션하는 방법 (배열의 여러 차원에 적용 할 수 있음) : ( seq명령을 사용하려면 IFS''(SPACE = 기본값)으로 설정 해야합니다 . while ... do ...또는 do ... while ...대신 이것을 피하기 위해 반복합니다 ( seq코드의 기능에 대한 더 나은 설명의 범위를 유지 했습니다).

#!/bin/sh

## The following functions implement vectors (arrays) operations in dash:
## Definition of a vector <v>:
##      v_0 - variable that stores the number of elements of the vector
##      v_1..v_n, where n=v_0 - variables that store the values of the vector elements

VectorAddElementNext () {
# Vector Add Element Next
# Adds the string contained in variable $2 in the next element position (vector length + 1) in vector $1

    local elem_value
    local vector_length
    local elem_name

    eval elem_value=\"\$$2\"
    eval vector_length=\$$1\_0
    if [ -z "$vector_length" ]; then
        vector_length=$((0))
    fi

    vector_length=$(( vector_length + 1 ))
    elem_name=$1_$vector_length

    eval $elem_name=\"\$elem_value\"
    eval $1_0=$vector_length
}

VectorAddElementDVNext () {
# Vector Add Element Direct Value Next
# Adds the string $2 in the next element position (vector length + 1) in vector $1

    local elem_value
    local vector_length
    local elem_name

    eval elem_value="$2"
    eval vector_length=\$$1\_0
    if [ -z "$vector_length" ]; then
        vector_length=$((0))
    fi

    vector_length=$(( vector_length + 1 ))
    elem_name=$1_$vector_length

    eval $elem_name=\"\$elem_value\"
    eval $1_0=$vector_length
}

VectorAddElement () {
# Vector Add Element
# Adds the string contained in the variable $3 in the position contained in $2 (variable or direct value) in the vector $1

    local elem_value
    local elem_position
    local vector_length
    local elem_name

    eval elem_value=\"\$$3\"
    elem_position=$(($2))
    eval vector_length=\$$1\_0
    if [ -z "$vector_length" ]; then
        vector_length=$((0))
    fi

    if [ $elem_position -ge $vector_length ]; then
        vector_length=$elem_position
    fi

    elem_name=$1_$elem_position

    eval $elem_name=\"\$elem_value\"
    if [ ! $elem_position -eq 0 ]; then
        eval $1_0=$vector_length
    fi
}

VectorAddElementDV () {
# Vector Add Element
# Adds the string $3 in the position $2 (variable or direct value) in the vector $1

    local elem_value
    local elem_position
    local vector_length
    local elem_name

    eval elem_value="$3"
    elem_position=$(($2))
    eval vector_length=\$$1\_0
    if [ -z "$vector_length" ]; then
        vector_length=$((0))
    fi

    if [ $elem_position -ge $vector_length ]; then
        vector_length=$elem_position
    fi

    elem_name=$1_$elem_position

    eval $elem_name=\"\$elem_value\"
    if [ ! $elem_position -eq 0 ]; then
        eval $1_0=$vector_length
    fi
}

VectorPrint () {
# Vector Print
# Prints all the elements names and values of the vector $1 on sepparate lines

    local vector_length

    vector_length=$(($1_0))
    if [ "$vector_length" = "0" ]; then
        echo "Vector \"$1\" is empty!"
    else
        echo "Vector \"$1\":"
        for i in $(seq 1 $vector_length); do
            eval echo \"[$i]: \\\"\$$1\_$i\\\"\"
            ###OR: eval printf \'\%s\\\n\' \"[\$i]: \\\"\$$1\_$i\\\"\"
        done
    fi
}

VectorDestroy () {
# Vector Destroy
# Empties all the elements values of the vector $1

    local vector_length

    vector_length=$(($1_0))
    if [ ! "$vector_length" = "0" ]; then
        for i in $(seq 1 $vector_length); do
            unset $1_$i
        done
        unset $1_0
    fi
}

##################
### MAIN START ###
##################

## Setting vector 'params' with all the parameters received by the script:
for i in $(seq 1 $#); do
    eval param="\${$i}"
    VectorAddElementNext params param
done

# Printing the vector 'params':
VectorPrint params

read temp

## Setting vector 'params2' with the elements of the vector 'params' in reversed order:
if [ -n "$params_0" ]; then
    for i in $(seq 1 $params_0); do
        count=$((params_0-i+1))
        VectorAddElement params2 count params_$i
    done
fi

# Printing the vector 'params2':
VectorPrint params2

read temp

## Getting the values of 'params2'`s elements and printing them:
if [ -n "$params2_0" ]; then
    echo "Printing the elements of the vector 'params2':"
    for i in $(seq 1 $params2_0); do
        eval current_elem_value=\"\$params2\_$i\"
        echo "params2_$i=\"$current_elem_value\""
    done
else
    echo "Vector 'params2' is empty!"
fi

read temp

## Creating a two dimensional array ('a'):
for i in $(seq 1 10); do
    VectorAddElement a 0 i
    for j in $(seq 1 8); do
        value=$(( 8 * ( i - 1 ) + j ))
        VectorAddElementDV a_$i $j $value
    done
done

## Manually printing the two dimensional array ('a'):
echo "Printing the two-dimensional array 'a':"
if [ -n "$a_0" ]; then
    for i in $(seq 1 $a_0); do
        eval current_vector_lenght=\$a\_$i\_0
        if [ -n "$current_vector_lenght" ]; then
            for j in $(seq 1 $current_vector_lenght); do
                eval value=\"\$a\_$i\_$j\"
                printf "$value "
            done
        fi
        printf "\n"
    done
fi

################
### MAIN END ###
################

1
그 동안 주 local모두에서 지원 bash하고 dash는 POSIX되지 않습니다. seqPOSIX 명령도 아닙니다. 코드가 $ IFS의 현재 값에 대해 약간의 가정을한다고 언급해야 할 것입니다 ( seq변수를 사용하지 않고 인용 하면 피할 수 있습니다)
Stéphane Chazelas
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.