Matrices en Unix Bourne Shell


26

Estoy tratando de usar matrices en Bourne shell ( /bin/sh). Descubrí que la forma de inicializar los elementos de la matriz es:

arr=(1 2 3)

Pero se encuentra con un error:

syntax error at line 8: `arr=' unexpected

Ahora, la publicación donde encontré esta sintaxis dice que es para bash, pero no pude encontrar ninguna sintaxis separada para el shell Bourne. ¿La sintaxis también es la misma /bin/sh?


1
compruebe esta pregunta stackoverflow.com/questions/9481702/… en desbordamiento de pila
Nischay

1
Thnx @Nischay ... Después de leer el enlace que proporcionó, refiné mi cadena de consulta en Google y obtuve el enlace - docstore.mik.ua/orelly/unix/upt/ch45_34.htm
SubhasisM

Respuestas:


47

/bin/shCasi nunca es un shell Bourne en ningún sistema hoy en día (incluso Solaris, que fue uno de los últimos sistemas importantes en incluirlo, ahora ha cambiado a POSIX sh por su / bin / sh en Solaris 11). /bin/shfue la concha de Thompson a principios de los 70 El shell Bourne lo reemplazó en Unix V7 en 1979.

/bin/sh ha sido el shell Bourne durante muchos años a partir de entonces (o el shell Almquist, una reimplementación gratuita en BSD).

Hoy en día, /bin/shes más comúnmente un intérprete u otro para el shlenguaje POSIX que se basa en un subconjunto del lenguaje de ksh88 (y un superconjunto del lenguaje shell Bourne con algunas incompatibilidades).

El shell Bourne o la especificación del lenguaje POSIX sh no admiten matrices. O, más bien que sólo tienen una matriz: los parámetros posicionales ( $1, $2, $@, por lo que una matriz por la función, así).

ksh88 tenía matrices con las que configuró set -A, pero eso no se especificó en POSIX sh ya que la sintaxis es incómoda y no muy utilizable.

Otros conchas con variables de matriz / listas incluyen: csh/ tcsh, rc, es, bash(que en su mayoría copiado la sintaxis ksh la forma ksh93), yash, zsh, fishcada uno con una sintaxis diferente ( rcla cáscara de la vez sea-sucesor de Unix, fishy zshsiendo el más consistente unos) ...

En estándar sh(también funciona en versiones modernas del 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)

(tenga en cuenta que en el shell Bourne y ksh88, $IFSdebe contener el carácter de espacio para "$@"que funcione correctamente (un error), y en el shell Bourne, no puede acceder a los elementos anteriores $9( ${10}no funcionará, aún puede hacer shift 1; echo "$9"o recorrer) ellos)).


2
Muchas gracias ... su explicación detallada fue muy útil.
SubhasisM

1
Vale la pena señalar que los parámetros posicionales difieren de las matrices bash en algunas características clave. Por ejemplo, no admiten matrices dispersas, y dado que sh no tiene expansión de parámetros de corte, no puede acceder a sublistas como "${@:2:4}". Para estar seguro, veo las similitudes , pero no considero los parámetros posicionales como una matriz per se.
kojiro

@kojiro, hasta cierto punto, yo diría que es al contrario, "$@"actúa como una matriz (como las matrices de csh, rc, zsh, fish, yash...), es más los Korn / bash "conjuntos" que no son realmente matrices, pero algunos forma de matrices asociativas con claves limitadas a enteros positivos (también tienen índices que comienzan en 0 en lugar de 1 como en todos los demás shells con matrices y "$ @"). Los shells que tienen soporte para segmentar pueden dividir $ @ de la misma manera (con ksh93 / bash agregando torpemente $ 0 a los parámetros posicionales cuando se divide "$ @").
Stéphane Chazelas

3

No hay matrices en el shell Bourne simple. Puede usar la siguiente forma para crear una matriz y recorrerla:

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

No importa en qué forma utilizar matrices sh, si elige, siempre será engorroso. Considere usar un idioma diferente, como Pythono Perlsi puede, a menos que esté atrapado en una plataforma muy limitada o quiera aprender algo.


Gracias por la respuesta...!! De hecho, estoy tratando de aprender cosas en el script de shell ... de lo contrario, implementar array en Python es realmente pan comido. Esta fue una gran lección de que existe un lenguaje de secuencias de comandos que no admite la matriz :) Una cosa, el código que publicaste está dando un error - "error de sintaxis en la línea 6:` $ 'inesperado "... Estoy un poco ocupado ahora, lo resolvería ... por favor, no te molestes.
SubhasisM

@NoobGeek, el shell Bourne no tiene la $(...)sintaxis. Por lo tanto, debe tener el shell Bourne. ¿Estás en Solaris 10 o antes? Lo más probable es que tampoco tengas sequno. En Solaris 10 y versiones anteriores, desea que / usr / xpg4 / bin / sh tenga un estándar en shlugar de un shell Bourne. Usar seqese camino tampoco es muy bueno.
Stéphane Chazelas

POSIX establece que $ y `son equivalentes en la sustitución de comandos: enlace . ¿Y por qué seqno es bueno usar de esa manera?
Arkadiusz Drabczyk

2
Sí en conchas POSIX, se debe preferir $(...)más `, pero el PO de /bin/shes probablemente una cáscara de Bourne, no un shell POSIX. Además de seqno ser un comando estándar, hacerlo $(seq 100)significa almacenar toda la salida en la memoria, y eso significa que depende del valor actual de $ IFS que contiene nueva línea y no contiene dígitos. Mejor usar i=1; while [ "$i" -le 100 ]; ...; i=$(($i + 1)); done(aunque eso tampoco funcionaría en el shell Bourne).
Stéphane Chazelas

1
@Daenyth Yo diría todo lo contrario: aprender primero los basismos, y luego la /bin/shsintaxis portátil , tiende a hacer que la gente piense que está bien usar el #!/bin/shshebang incorrecto , y luego rompe sus scripts cuando otras personas intentan usarlos. Se recomienda no publicar este tipo de cebo de llama. :)
Josip Rodin

2

Como han dicho los demás, Bourne Shell no tiene matrices verdaderas .

Sin embargo, dependiendo de lo que necesite hacer, las cadenas delimitadas deberían ser suficientes:

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

Si los delimitadores típicos (espacio, tabulación y nueva línea) no son suficientes, puede establecer el IFSdelimitador que desee antes del bucle.

Y si necesita compilar la matriz mediante programación, puede crear una cadena delimitada.


1
A menos que lo desee (poco probable), es probable que también desee deshabilitar el globbing, que es otro efecto de dejar variables sin comillas como esa (el split+globoperador).
Stéphane Chazelas

0

Una forma de simular matrices en el tablero (se puede adaptar para cualquier cantidad de dimensiones de una matriz): (Tenga en cuenta que el uso del seqcomando requiere que IFSesté configurado en '' (ESPACIO = el valor predeterminado). Puede usar while ... do ...o do ... while ...bucles en su lugar para evitar esto (mantuve seqen el alcance de una mejor ilustración de lo que hace el código).

#!/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
Tenga en cuenta que while locales compatible con ambos bashy dashno es POSIX. seqtampoco es un comando POSIX. Probablemente debería mencionar que su código hace algunas suposiciones sobre el valor actual de $ IFS (si evita usar seqy cotiza sus variables, se puede evitar)
Stéphane Chazelas
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.