Matrici in Unix Bourne Shell


26

Sto cercando di utilizzare gli array nella shell Bourne ( /bin/sh). Ho scoperto che il modo per inizializzare gli elementi dell'array è:

arr=(1 2 3)

Ma si verifica un errore:

syntax error at line 8: `arr=' unexpected

Ora il post in cui ho trovato questa sintassi dice che lo è bash, ma non sono riuscito a trovare alcuna sintassi separata per la shell Bourne. Anche la sintassi è la stessa /bin/sh?


1
controlla questa domanda stackoverflow.com/questions/9481702/… su stack overflow
Nischay

1
Thnx @Nischay ... Dopo aver letto il link che hai fornito, ho affinato la mia stringa di ricerca in google ed ha ottenuto il link - docstore.mik.ua/orelly/unix/upt/ch45_34.htm
SubhasisM

Risposte:


47

/bin/shal giorno d'oggi non è quasi mai una shell Bourne su alcun sistema (anche Solaris, che è stato uno degli ultimi sistemi principali ad includere, è ora passato a un POSIX sh per il suo / bin / sh in Solaris 11). /bin/shera la conchiglia Thompson nei primi anni '70. La shell Bourne lo sostituì in Unix V7 nel 1979.

/bin/sh è stata la shell Bourne per molti anni in seguito (o la shell Almquist, una reimplementazione gratuita su BSD).

Al giorno d'oggi, /bin/shè più comunemente un interprete o un altro per il shlinguaggio POSIX che è esso stesso basato su un sottoinsieme del linguaggio di ksh88 (e un superset del linguaggio shell Bourne con alcune incompatibilità).

La shell Bourne o la specifica del linguaggio sh POSIX non supportano gli array. Anzi hanno solo una matrice: i parametri posizionali ( $1, $2, $@, quindi un array per funzione pure).

ksh88 aveva array con cui si impostava set -A, ma ciò non è stato specificato in POSIX sh poiché la sintassi è scomoda e non molto utilizzabile.

Altre shell con variabili di campo / elenchi includono: csh/ tcsh, rc, es, bash(che in gran parte copiata la sintassi ksh modo ksh93), yash, zsh, fishciascuno con una sintassi diversa ( rcil guscio della volta per-be successore di Unix, fished zshessendo la più coerente quelli ...)

In standard sh(funziona anche nelle versioni moderne della 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)

(nota che nella shell Bourne e ksh88, $IFSdeve contenere il carattere spazio per "$@"funzionare correttamente (un bug), e nella shell Bourne, non puoi accedere agli elementi sopra $9( ${10}non funzionerà, puoi comunque fare shift 1; echo "$9"o passare sopra loro)).


2
Grazie mille ... la tua spiegazione dettagliata è stata molto utile.
Sottofase

1
Vale la pena notare che i parametri posizionali differiscono dagli array bash in alcune caratteristiche chiave. Ad esempio, non supportano matrici sparse e poiché sh non ha l'espansione dei parametri di divisione, non è possibile accedere a liste secondarie come "${@:2:4}". A dire il vero , vedo le somiglianze , ma non considero i parametri posizionali come un array di per sé.
Kojiro,

@kojiro, in una certa misura, io direi che è, al contrario, "$@"agisce come un array (come gli array di csh, rc, zsh, fish, yash...), è più i Korn / bash "array" che non sono realmente gli array, ma un po ' forma di array associativi con chiavi limitate a numeri interi positivi (hanno anche indici che iniziano da 0 invece di 1 come in tutte le altre shell con array e "$ @"). Le shell che supportano lo slicing possono tagliare $ @ allo stesso modo (con ksh93 / bash che aggiunge goffamente $ 0 ai parametri posizionali quando si suddivide "$ @").
Stéphane Chazelas,

3

Non ci sono array nella semplice shell Bourne. È possibile utilizzare il seguente modo per creare un array e attraversarlo:

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

Indipendentemente dal modo in cui utilizzare gli array sh, sceglierli sarà sempre ingombrante. Prendi in considerazione l'uso di una lingua diversa, come Pythono Perlse puoi, a meno che tu non sia bloccato con una piattaforma molto limitata o desideri imparare qualcosa.


Grazie per la risposta...!! In realtà sto davvero cercando di imparare cose nello script di shell ... altrimenti implementare array in Python è davvero un gioco da ragazzi. Questa è stata una grande lezione che esiste un linguaggio di scripting che non supporta l'array :) Una cosa, il codice che hai pubblicato sta dando un errore - "errore di sintassi alla riga 6:` $ 'imprevisto "... Sono un po' occupato ora, vorrei risolverlo ... per favore non preoccuparti.
Sottofase

@NoobGeek, la shell Bourne non ha la $(...)sintassi. Quindi devi davvero avere la shell Bourne. Sei su Solaris 10 o prima? È probabile che non avrai seqneanche uno. Su Solaris 10 e precedenti, si desidera che / usr / xpg4 / bin / sh disponga di uno standard shanziché di una shell Bourne. Anche in seqquesto modo non è molto buono.
Stéphane Chazelas,

POSIX afferma che $ e `sono equivalenti nella sostituzione dei comandi: link . E perché usare in seqquel modo non è buono?
Arkadiusz Drabczyk,

2
Sì nella shell POSIX, si dovrebbe preferisce $(...)sopra `, ma il PO di /bin/shè probabilmente una shell Bourne, non una shell POSIX. Oltre a seqnon essere un comando standard, fare $(seq 100)significa memorizzare l'intero output in memoria, e ciò significa che dipende dal valore corrente di $ IFS contenente newline e non contenente cifre. Meglio usare i=1; while [ "$i" -le 100 ]; ...; i=$(($i + 1)); done(anche se non funzionerebbe nemmeno nella shell Bourne).
Stéphane Chazelas,

1
@Daenyth Direi piuttosto il contrario: apprendere prima i basismi e poi la /bin/shsintassi portatile , tende a far pensare alle persone che è giusto usare lo #!/bin/shshebang sbagliato , e poi rompe i loro script quando altre persone cercano di usarli. Ti consigliamo di non pubblicare questo tipo di esca di fiamma. :)
Josip Rodin,

2

Come hanno detto gli altri, la Bourne Shell non ha array veri .

Tuttavia, a seconda di ciò che è necessario fare, le stringhe delimitate dovrebbero essere sufficienti:

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

Se i delimitatori tipici (spazio, tabulazione e nuova riga) non sono sufficienti, puoi impostare il IFSdelimitatore desiderato prima del ciclo.

E se hai bisogno di costruire l'array a livello di codice, puoi semplicemente creare una stringa delimitata.


1
A meno che tu non lo desideri (improbabile), probabilmente vorrai anche disabilitare il globbing che è un altro effetto di lasciare variabili non quotate in questo modo (l' split+globoperatore).
Stéphane Chazelas,

0

Un modo per simulare le matrici nel trattino (può essere adattato per qualsiasi numero di dimensioni di una matrice): (Si noti che l'uso del seqcomando richiede che IFSsia impostato su '' (SPAZIO = il valore predefinito). È possibile utilizzare while ... do ...o do ... while ...loop invece per evitarlo (mi sono tenuto seqnell'ambito di una migliore illustrazione di ciò che fa il codice).)

#!/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
Si noti che mentre localè supportato da entrambi bashe dash, non è POSIX. seqnon è neanche un comando POSIX. Probabilmente dovresti menzionare che il tuo codice fa alcune ipotesi sul valore corrente di $ IFS (se eviti di usare seqe citare le tue variabili, può essere evitato)
Stéphane Chazelas
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.