Unix Bourne Shell 中的陣列

Unix Bourne Shell 中的陣列

我正在嘗試在 Bourne shell ( /bin/sh) 中使用陣列。我發現初始化數組元素的方法是:

arr=(1 2 3)

但它遇到了一個錯誤:

syntax error at line 8: `arr=' unexpected

現在,我發現此語法的帖子說它是 for bash,但我找不到 Bourne shell 的任何單獨語法。文法/bin/sh也一樣嗎?

答案1

/bin/sh如今,在任何系統上幾乎都沒有 Bourne shell(即使是最後一個包含它的主要係統之一的 Solaris,現在也已將 Solaris 11 中的 /bin/sh 切換為 POSIX sh)。/bin/sh是70年代初的湯普森砲彈。 1979 年,Bourne shell 在 Unix V7 中取代了它。

/bin/sh此後多年一直是 Bourne shell(或 Almquist shell,BSD 上的免費重新實現)。

如今,/bin/sh更常見的是 POSIXsh語言的解釋器或其他解釋器,該語言本身基於 ksh88 語言的子集(以及 Bourne shell 語言的超集,但存在一些不相容性)。

Bourne shell 或 POSIX sh 語言規範不支援陣列。或者更確切地說,它們只有一個陣列:位置參數($1$2$@,因此每個函數也有一個陣列)。

ksh88 確實有您使用 設定的數組set -A,但沒有在 POSIX sh 中指定,因為語法很尷尬且不太可用。

其他具有陣列/列表變數的 shell 包括:csh/ tcshrcesbash(主要以 ksh93 方式複製 ksh 語法)、yashzshfish每個都有不同的語法(rcUnix 的曾經的後繼者的 shell,fish並且zsh是最一致的那些) …

在標準中sh(也適用於現代版本的 Bourne shell):

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 (except zsh, when not in sh emulation and
                   # most ash-based 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 shell 和 ksh88 中,$IFS必須包含空格字元才能"$@"正常工作(一個錯誤),並且在 Bourne shell 中,您無法存取上面的元素$9${10}不起作用,您仍然可以執行shift 1; echo "$9"或循環他們))。

答案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循環之前您想要的任何分隔符號。

如果您需要以程式設計方式建構數組,則只需建立分隔字串即可。

答案3

普通 Bourne shell 中沒有陣列。可以使用以下方式建立數組並遍歷它:

#!/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除非您受限於非常有限的平台或想要學習一些東西。

答案4

在破折號中模擬數組的方法(它可以適應數組的任意維數):(請注意,使用該seq命令需要將其IFS設為“ ”(空格=預設值)。您可以使用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 ###
################

相關內容