Unix Bourne Shellの配列


26

Bourneシェル(/bin/sh)で配列を使用しようとしています。配列要素を初期化する方法は次のとおりです。

arr=(1 2 3)

しかし、エラーが発生しています:

syntax error at line 8: `arr=' unexpected

今、私がこの構文を見つけた投稿はそれがのためbashであると言いますが、私はBourneシェルのための別の構文を見つけることができませんでした。構文も同じ/bin/shですか?


1
この質問を確認してくださいstackoverflow.com/questions/9481702/…スタックオーバーフローについて
Nischay 14年

1
Thnx @Nischay ...提供されたリンクを読んだ後、Googleでクエリ文字列を改良し、リンクを取得しました-docstore.mik.ua/orelly/unix/upt/ch45_34.htm
SubhasisM

回答:


47

/bin/sh最近のシステムではほとんどBourneシェルではありません(これを含む最後の主要なシステムの1つであったSolarisでさえ、Solaris 11では/ bin / shのPOSIX shに切り替わりました)。/bin/sh70年代初期のトンプソン砲弾でした。Bourneシェルは、1979年にUnix V7で置き換えられました。

/bin/sh その後、長年にわたってBourneシェル(またはBSDでの無料の再実装であるAlmquistシェル)でした。

現在で/bin/shsh、ksh88の言語のサブセット(および非互換性のあるBourneシェル言語のスーパーセット)自体に基づいているPOSIX 言語のインタープリターまたは別の言語がより一般的です。

BourneシェルまたはPOSIX sh言語仕様は配列をサポートしていません。むしろ、それらは唯一の配列を有する:位置パラメータ($1$2$@、機能ごとにアレイによくように)。

ksh88にはset -A、で設定した配列がありましたが、構文が厄介であまり使用できないため、POSIX shでは指定されませんでした。

配列/リストの変数を他のシェルは:csh/ tcshrcesbash(ほとんどkshの構文には、ksh93の方法をコピーし)、 、、yash 異なる構文(各のUnixの一度に被後継の殻、及び最も一貫性のあることをもの)...zshfishrcfishzsh

標準で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 14年

1
いくつかの重要な機能では、位置パラメータがbash配列と異なることに注意する価値があります。たとえば、スパース配列をサポートしていません。また、shにはスライスパラメーターの展開がないため、などのサブリストにアクセスできません"${@:2:4}"。確かに、私は類似点を見ていますが、位置パラメータを配列そのものとは考えていません。
小次郎

@kojiroは、ある程度まで、私はそれは逆だと言うだろう、"$@"(の配列のような配列のように機能しcshrczshfishyash...)、それはより実際の配列ではありませんKornシェル/ bashの「配列」ですが、いくつかの正の整数に制限されたキーを持つ連想配列の形式(配列と "$ @"を持つ他のすべてのシェルのように、1の代わりに0から始まるインデックスもあります)。スライスをサポートするシェルは、$ @をまったく同じようにスライスできます(ksh93 / bashでは、 "$ @"をスライスするときに位置パラメーターに$ 0を不注意に追加します)。
ステファンシャゼラス

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可能であれば別の言語の使用を検討してください。


回答ありがとうございます... !! 実際、私は実際にシェルスクリプトで物事を学ぼうとしています...そうでなければ、Pythonで配列を実装するのは本当に簡単です。これは、配列をサポートしないスクリプト言語が存在するという大きな教訓でした:) 1つ、あなたが投稿したコードはエラーを与えています-「6行目の構文エラー: '$'予期しない」...私は少し忙しいです今、私はそれを解決するだろう... plzは気にしないでください。
SubhasisM 14年

@ NoobGeek、Bourneシェルには$(...)構文がありません。したがって、実際にはBourneシェルが必要です。あなたはSolaris 10以前ですか?seqどちらかを持たない可能性があります。Solaris 10以前でshは、Bourneシェルの代わりに/ usr / xpg4 / bin / shに標準が必要です。seqその方法を使用することもあまり良くありません。
ステファンシャゼル14年

POSIXは、コマンド置換で$と `が同等であると述べています:link。そして、なぜseqそのように使うのが良くないのですか?
はArkadiusz Drabczyk

2
はいPOSIXシェルでは、あなたが選ぶべき$(...)超える`が、OPのは、/bin/shおそらく、Bourneシェルではなく、POSIXシェルです。seq標準コマンドではないことに加えて、行う$(seq 100)ことは出力全体をメモリに保存することを意味し、それは改行を含み数字を含まない$ IFSの現在の値に依存することを意味します。使用するのが最適i=1; while [ "$i" -le 100 ]; ...; i=$(($i + 1)); doneです(ただし、Bourneシェルでも機能しません)。
ステファンシャゼル14年

1
@Daenyth私はまったく逆に言うでしょう:最初にバシズムを学び、次に移植可能な/bin/sh構文は、間違った#!/bin/shシバンを使用しても大丈夫だと人々に思わせ、他の人がそれらを使用しようとするとスクリプトを壊します。この種のフレームベイトを投稿しないことをお勧めします。:)
ロダン

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
ステファンシャゼル

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あなたの変数を引用して、それを回避することができます)
ステファンChazelas
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.