Array dalam Unix Bourne Shell

26

Saya mencoba menggunakan array di shell Bourne ( /bin/sh). Saya menemukan bahwa cara untuk menginisialisasi elemen array adalah:

arr=(1 2 3)

Tetapi sedang mengalami kesalahan:

syntax error at line 8: `arr=' unexpected

Sekarang posting di mana saya menemukan sintaks ini mengatakan itu untuk bash, tetapi saya tidak dapat menemukan sintaks yang terpisah untuk shell Bourne. Apakah sintaksinya juga sama /bin/sh?

SubhasisM
sumber
1
periksa pertanyaan ini stackoverflow.com/questions/9481702/… on stack overflow
Nischay
1
Thnx @Nischay ... Setelah membaca tautan yang Anda berikan, saya memperbaiki string kueri saya di google dan mendapatkan tautan - docstore.mik.ua/orelly/unix/upt/ch45_34.htm
SubhasisM

Jawaban:

47

/bin/shhampir tidak pernah menjadi shell Bourne pada sistem apa pun saat ini (bahkan Solaris yang merupakan salah satu sistem utama terakhir yang menyertakannya kini telah beralih ke POSIX sh untuk / bin / sh di Solaris 11). /bin/shadalah cangkang Thompson di awal 70-an. Shell Bourne menggantinya di Unix V7 pada tahun 1979.

/bin/sh telah menjadi cangkang Bourne selama bertahun-tahun sesudahnya (atau cangkang Almquist, implementasi ulang gratis pada BSD).

Saat ini, /bin/shlebih umum penerjemah atau yang lain untuk shbahasa POSIX yang itu sendiri didasarkan pada subset dari bahasa ksh88 (dan superset dari bahasa shell Bourne dengan beberapa ketidakcocokan).

Shell Bourne atau spesifikasi bahasa POSIX sh tidak mendukung array. Atau lebih tepatnya mereka hanya memiliki satu array: parameter posisi ( $1, $2, $@, sehingga array satu per fungsi juga).

ksh88 memang memiliki array yang Anda set dengan set -A, tetapi itu tidak bisa ditentukan dalam POSIX sh karena sintaksinya canggung dan tidak bisa digunakan.

Kerang lain dengan variabel array / daftar meliputi: csh/ tcsh, rc, es, bash(yang sebagian besar disalin sintaks ksh cara ksh93), yash, zsh, fishmasing-masing dengan sintaks yang berbeda ( rccangkang sekali to-be penerus Unix, fishdan zshyang paling konsisten yang) ...

Dalam standar sh(juga berfungsi dalam versi modern dari shell 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)

(perhatikan bahwa di shell Bourne dan ksh88, $IFSharus berisi karakter spasi untuk "$@"berfungsi dengan benar (bug), dan di shell Bourne, Anda tidak dapat mengakses elemen di atas $9( ${10}tidak akan berfungsi, Anda masih bisa melakukan shift 1; echo "$9"atau mengulang mereka)).

Stéphane Chazelas
sumber
2
Terima kasih banyak ... penjelasan terperinci Anda sangat membantu.
SubhasisM
1
Mungkin perlu dicatat bahwa parameter posisi berbeda dari bash array di beberapa fitur utama. Misalnya, mereka tidak mendukung array jarang, dan karena sh tidak memiliki perluasan parameter slicing, Anda tidak dapat mengakses sublists seperti "${@:2:4}". Yang pasti, saya melihat kesamaan , tapi saya tidak menganggap parameter posisi sebagai array per se.
kojiro
@kojiro, sampai batas tertentu, saya akan mengatakan itu sebaliknya, "$@"bertindak seperti sebuah array (seperti array csh, rc, zsh, fish, yash...), itu lebih Korn / bash "array" yang tidak benar-benar array, tetapi beberapa bentuk array asosiatif dengan kunci terbatas pada bilangan bulat positif (mereka juga memiliki indeks mulai dari 0 bukannya 1 seperti di semua shell lain dengan array dan "$ @"). Shell yang memiliki dukungan untuk mengiris dapat mengiris $ @ sama saja (dengan ksh93 / bash menambahkan $ 0 ke parameter posisi dengan canggung ketika Anda mengiris "$ @").
Stéphane Chazelas
3

Tidak ada array di shell Bourne polos. Anda bisa menggunakan cara berikut untuk membuat array dan melewatinya:

#!/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

Tidak peduli bagaimana cara menggunakan array yang shAnda pilih, itu akan selalu rumit. Pertimbangkan untuk menggunakan bahasa yang berbeda seperti Pythonatau Perljika Anda bisa kecuali Anda terjebak dengan platform yang sangat terbatas atau ingin belajar sesuatu.

Arkadiusz Drabczyk
sumber
Terima kasih atas tanggapannya ... !! Sebenarnya saya memang mencoba mempelajari hal-hal dalam skrip shell ... jika tidak mengimplementasikan array dengan Python benar-benar sepotong kue. Ini adalah pelajaran besar bahwa ada beberapa bahasa scripting yang tidak mendukung array :) Satu hal, kode yang Anda posting memberikan kesalahan - "kesalahan sintaks pada baris 6:` $ 'tidak terduga "... Saya agak sibuk sekarang, saya akan menyelesaikannya ... tolong jangan repot-repot.
SubhasisM
@NoobGeek, shell Bourne tidak memiliki $(...)sintaksis. Jadi, Anda memang harus memiliki kulit Bourne. Apakah Anda menggunakan Solaris 10 atau sebelumnya? Kemungkinannya adalah Anda tidak akan memiliki seqkeduanya. Pada Solaris 10 dan sebelumnya, Anda ingin / usr / xpg4 / bin / sh memiliki standar, shbukan shell Bourne. Menggunakan seqcara itu juga tidak terlalu baik.
Stéphane Chazelas
POSIX menyatakan bahwa $ dan `sama dengan substitusi perintah: tautan . Dan mengapa menggunakan seqcara itu tidak baik?
Arkadiusz Drabczyk
2
Ya di shell POSIX, Anda harus memilih di $(...)atas `, tetapi OP /bin/shmungkin shell Bourne, bukan shell POSIX. Selain seqtidak menjadi perintah standar, melakukan $(seq 100)berarti menyimpan seluruh output dalam memori, dan itu berarti itu tergantung pada nilai saat ini dari $ IFS yang mengandung baris baru dan tidak mengandung angka. Terbaik untuk digunakan i=1; while [ "$i" -le 100 ]; ...; i=$(($i + 1)); done(meskipun itu tidak akan bekerja di shell Bourne baik).
Stéphane Chazelas
1
@Daenyth saya akan mengatakan sebaliknya: mempelajari bashism terlebih dahulu, dan kemudian /bin/shsintaks portabel kemudian, cenderung membuat orang berpikir bahwa boleh saja menggunakan #!/bin/shshebang yang salah , dan kemudian memecah skrip mereka ketika orang lain mencoba menggunakannya. Anda disarankan untuk tidak memposting jenis flamebait ini. :)
Josip Rodin
2

Seperti yang dikatakan orang lain, Shell Bourne tidak memiliki array yang benar .

Namun, tergantung pada apa yang perlu Anda lakukan, string yang dibatasi harus mencukupi:

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

Jika pembatas tipikal (spasi, tab, dan baris baru) tidak cukup, Anda dapat mengatur IFSpembatas apa pun yang Anda inginkan sebelum loop.

Dan jika Anda perlu membangun array secara terprogram, Anda bisa membangun string yang dibatasi.

Sildoreth
sumber
1
Kecuali Anda memang menginginkannya (tidak mungkin), Anda mungkin juga ingin menonaktifkan globbing yang merupakan efek lain dari membiarkan variabel tidak dikutip seperti itu ( split+globoperator).
Stéphane Chazelas
0

Cara untuk mensimulasikan array di dash (dapat diadaptasi untuk sejumlah dimensi array): (Harap dicatat bahwa penggunaan seqperintah mengharuskan yang IFSdiatur ke '' (SPACE = nilai default). Anda dapat menggunakan while ... do ...atau do ... while ...loops sebagai gantinya untuk menghindari ini (saya menyimpannya seqdalam lingkup ilustrasi yang lebih baik tentang apa yang dilakukan kode).)

#!/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 ###
################

sumber
1
Perhatikan bahwa sementara localdidukung oleh keduanya bashdan dash, itu bukan POSIX. seqjuga bukan perintah POSIX. Anda mungkin harus menyebutkan bahwa kode Anda membuat beberapa asumsi pada nilai saat ini dari $ IFS (jika Anda menghindari menggunakan seqdan mengutip variabel-variabel Anda, itu dapat dihindari)
Stéphane Chazelas