¿Bash proporciona soporte para usar punteros?

12

Pregunta simple. ¿El shell bash tiene algún soporte para usar punteros al escribir un script de shell?

Estoy familiarizado con la notación de expansión, ${var[@]}al iterar sobre la matriz $var, pero no está claro que esto esté utilizando punteros para iterar sobre los índices de la matriz. ¿Bash proporciona acceso a direcciones de memoria como otros idiomas?

Si bash no admite el uso de punteros, ¿qué hacen otros shells?

111 ---
fuente

Respuestas:

28

Un puntero (a una ubicación de memoria ) no es realmente un concepto útil en algo de nivel superior a C, ya sea algo como Python o el shell. Las referencias a objetos son, por supuesto, útiles en lenguajes de alto nivel, tal vez incluso necesarios para construir estructuras de datos complejas. Pero en la mayoría de los casos, pensar en términos de direcciones de memoria es un nivel demasiado bajo para ser muy útil.

En Bash (y otros shells), puede obtener los valores de los elementos de la matriz con la ${array[index]}notación, asignarlos array[index]=...y obtener el número de elementos en la matriz con ${#array[@]}. La expresión dentro de los corchetes es una expresión aritmética. Como ejemplo inventado, podríamos agregar un prefijo constante a todos los miembros de la matriz:

for ((i=0 ; i < ${#array[@]} ; i++ )) ; do
    array[i]="foo-${array[i]}"
done

(Si solo nos preocuparan los valores, y no los índices, for x in "${array[@]}" ; do...estaría bien).

Con matrices asociativas o dispersas , un bucle numérico no tiene mucho sentido, pero en su lugar tendríamos que buscar las claves / índices de la matriz ${!array[@]}. P.ej

declare -A assoc=([foo]="123" [bar]="456")
for i in "${!assoc[@]}" ; do 
    echo "${assoc[$i]}"
done 

Además de eso, Bash tiene dos formas de señalar indirectamente a otra variable:

  • expansión indirecta , usando la ${!var}sintaxis , que usa el valor de la variable cuyo nombre está en var, y
  • namerefs , que deben crearse con la función declareincorporada (o el kshsinónimo compatible typeset). declare -n ref=varhace refuna referencia a la variable var.

Namerefs también admite la indexación, ya que si lo hemos hecho arr=(a b c); declare -n ref=arr;, ${ref[1]}se expandirá a b. En su lugar, usar ${!p[1]}tomaría pcomo una matriz y se referiría a la variable nombrada por su segundo elemento.

En Bash, namerefs es literalmente eso, las referencias por nombre y el uso de un nameref desde dentro de una función usarán el valor local de la variable nombrada. Esto se imprimirá local value of var.

#!/bin/bash
fun() {
        local var="local value of var"
        echo "$ref";
}
var="global var"
declare -n ref=var
fun

BashFAQ también tiene un artículo más largo sobre indirección .

ilkkachu
fuente
2
la indirección es bastante útil en lenguajes de nivel superior. por ejemplo, referencias en perl. No son lo mismo que los punteros en C, pero cumplen la misma función básica. Incluso bash puede acceder a las variables indirectamente ... pero IMO si comienza a escribir código que hace un uso significativo de la función, es mejor comenzar desde cero con Perl o algo así. Véase también mywiki.wooledge.org/BashFAQ/006
cas el
2
@cas, oh, absolutamente. Pero probablemente sea mejor pensar en ellos como apuntando a objetos , en lugar de direcciones de memoria. (Incluso en C, hay un tipo involucrado). Tenía la intención de notar tanto la expansión indirecta como los nombres, pero no tuve tiempo para hacerlo de inmediato.
ilkkachu
Probablemente valga la pena señalar que el ejemplo for-loop está escrito de forma más natural a for foo in "${array[@]}" ; do ... donemenos que necesite el índice para otro (s) propósito (s).
Will Crawford
@ WillCrawford, punto. editado el ejemplo y anotado.
ilkkachu
9

No, bashno tiene "punteros", pero tiene referencias:

$ spam="fred"
$ declare -n tripe=spam
$ echo $tripe
fred
$ tripe=juki
$ echo $spam
juki

Desde la bashpágina del manual:

A una variable se le puede asignar el atributo nameref utilizando la opción -n a los comandos declareo localbuiltin para crear un nameref o una referencia a otra variable. Esto permite que las variables sean manipuladas indirectamente. Cada vez que se hace referencia a la variable nameref, se le asigna, se desarma o se modifican sus atributos (aparte de usar o cambiar el atributo nameref en sí), la operación se realiza realmente en la variable especificada por el valor de la variable nameref. Un nameref se usa comúnmente dentro de las funciones de shell para referirse a una variable cuyo nombre se pasa como argumento a la función. Por ejemplo, si se pasa un nombre de variable a una función de shell como primer argumento, ejecutar

declare -n ref=$1

dentro de la función crea una variable nameref ref cuyo valor es el nombre de la variable pasado como primer argumento. Las referencias y asignaciones a ref, y los cambios a sus atributos, se tratan como referencias, asignaciones y modificaciones de atributos a la variable cuyo nombre se pasó como $ 1. Si la variable de control en un bucle for tiene el atributo nameref, la lista de palabras puede ser una lista de variables de shell y, a su vez, se establecerá una referencia de nombre para cada palabra de la lista cuando se ejecute el bucle. Las variables de matriz no pueden recibir el atributo nameref. Sin embargo, las variables nameref pueden hacer referencia a variables de matriz y variables de matriz suscritas. Namerefs puede ser desarmado usando la opción -n para el unsetincorporado. De lo contrario, siunset se ejecuta con el nombre de una variable nameref como argumento, la variable referenciada por la variable nameref no se establecerá.

hackerb9
fuente
4

No, los shells no usan "punteros" (como se entiende en C).

Las matrices podrían usar índices: echo "${array[2]}"pero @en su ejemplo no es realmente un "puntero". Es una forma de expresar "la lista de valores de la matriz". Algo que entiende el analizador de shell. Similar a la forma en que:

$ echo "$@"

se expande a toda la lista de "Parámetros posicionales".

NotAnUnixNazi
fuente
2

Mientras que las matrices indexadas de bash integer se pueden definir y acceder de forma iterativa de esta manera;

declare -a obj
obj+=("val1")
obj+=("val2")

for item in ${obj[@]}; do
  echo "${obj[${item}]} ${item}"
done

Las matrices indexadas asociativas o basadas en cadenas en bash requieren la siguiente definición iterativa;

declare -A obj
obj["key1"]="val1"
obj["key2"]="val2"

for item in ${!obj[@]}; do
  echo "${obj[${item}]} ${item}"
done

Para responder la pregunta sobre punteros y usar uno de bash; la funcionalidad interna del binario bash compilado de hecho hace uso de punteros a la memoria asignada en la pila y expone una funcionalidad similar con el uso de eval. Ver [referencias indirectas] http://tldp.org/LDP/abs/html/ivr.html )

Habrá dragones; el uso de evaldebe usarse con precaución debido a implicaciones de seguridad

jas-
fuente