O bash fornece suporte para o uso de ponteiros?

12

Questão simples. O shell bash tem suporte para usar ponteiros ao escrever um script de shell?

Eu estou familiarizado com a notação de expansão, ${var[@]}ao iterar sobre a matriz $var, mas não está claro que isso esteja utilizando ponteiros para iterar sobre os índices da matriz. O bash fornece acesso a endereços de memória como outros idiomas?

Se o bash não suporta o uso de ponteiros, o que outras conchas fazem?

111 ---
fonte

Respostas:

28

Um ponteiro (para um local da memória ) não é realmente um conceito útil em algo de nível superior ao C, seja algo como Python ou o shell. Naturalmente, as referências a objetos são úteis em linguagens de alto nível, talvez até necessárias para a construção de estruturas de dados complexas. Mas, na maioria dos casos, pensar em termos de endereços de memória é um nível muito baixo para ser muito útil.

No Bash (e outros shells), você pode obter os valores dos elementos da matriz com a ${array[index]}notação, atribuí-los array[index]=...e obter o número de elementos na matriz ${#array[@]}. A expressão dentro dos colchetes é uma expressão aritmética. Como exemplo inventado, podemos adicionar um prefixo constante a todos os membros da matriz:

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

(Se apenas nos importássemos com os valores, e não com os índices, tudo for x in "${array[@]}" ; do...bem.)

Com matrizes associativas ou esparsas , um loop numérico não faz muito sentido, mas precisamos buscar as chaves / índices da matriz ${!array[@]}. Por exemplo

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

Além disso, o Bash tem duas maneiras de apontar indiretamente para outra variável:

  • expansão indireta , usando a ${!var}sintaxe , que usa o valor da variável cujo nome está vare
  • namerefs , que precisa ser criado com o declarebuiltin (ou o kshsinônimo compatível typeset). declare -n ref=varfaz refuma referência à variável var.

Os Namerefs também suportam indexação, pois, se tivermos arr=(a b c); declare -n ref=arr;, ${ref[1]}expandiremos para b. Em ${!p[1]}vez disso, usar pcomo matriz e se referir à variável nomeada por seu segundo elemento.

No Bash, namerefs é literalmente que, referências por nome e o uso de nameref de dentro de uma função usarão o valor local da variável nomeada. Isso será impresso local value of var.

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

O BashFAQ também tem um artigo mais longo sobre indireção .

ilkkachu
fonte
2
a indireção é bastante útil em idiomas de nível superior. por exemplo, referências em perl. Eles não são iguais aos ponteiros C, mas servem à mesma função básica. Até o bash pode acessar variáveis ​​indiretamente ... mas o IMO se você começar a escrever código que faz uso significativo do recurso, é melhor começar do zero com perl ou algo assim. Veja também mywiki.wooledge.org/BashFAQ/006
cas
2
@cas, oh, absolutamente. Mas é provavelmente melhor pensar neles como apontando para objetos do que para endereços de memória. (Mesmo em C, há um tipo envolvido.) Eu pretendia observar a expansão indireta e o namerefs, mas não tive tempo para fazê-lo imediatamente.
Ilkkachu
Provavelmente, vale a pena ressaltar que o exemplo do loop for é mais naturalmente escrito, a for foo in "${array[@]}" ; do ... donemenos que você precise do índice para outros fins.
Will Crawford
@WillCrawford, ponto. editou o exemplo e anotou.
Ilkkachu
9

Não, bashnão possui "ponteiros", mas possui referências:

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

Na bashpágina do manual:

Uma variável pode ser atribuída ao atributo nameref usando a opção -n para os comandos declareou localbuiltin para criar um nameref ou uma referência a outra variável. Isso permite que variáveis ​​sejam manipuladas indiretamente. Sempre que a variável nameref for referenciada, atribuída, desabilitada ou tiver seus atributos modificados (exceto usar ou alterar o próprio atributo nameref), a operação será realmente executada na variável especificada pelo valor da variável nameref. Um nameref é comumente usado nas funções de shell para se referir a uma variável cujo nome é passado como argumento para a função. Por exemplo, se um nome de variável for passado para uma função shell como seu primeiro argumento, executando

declare -n ref=$1

dentro da função cria uma variável nameref ref cujo valor é o nome da variável passado como o primeiro argumento. Referências e atribuições para ref e alterações em seus atributos são tratadas como referências, atribuições e modificações de atributos na variável cujo nome foi passado como $ 1. Se a variável de controle em um loop for tiver o atributo nameref, a lista de palavras poderá ser uma lista de variáveis ​​de shell e uma referência de nome será estabelecida para cada palavra da lista, por sua vez, quando o loop for executado. As variáveis ​​de matriz não podem receber o atributo nameref. No entanto, as variáveis ​​nameref podem fazer referência a variáveis ​​de matriz e variáveis ​​de matriz subscritas. Os Namerefs podem ser desabilitados usando a opção -n no unsetbuilt-in. Caso contrário, seunset for executado com o nome de uma variável nameref como argumento, a variável referenciada pela variável nameref será desativada.

hackerb9
fonte
4

Não, as conchas não usam "ponteiros" (como entendido em C).

As matrizes podem usar índices: echo "${array[2]}"mas o @exemplo não é realmente um "ponteiro". É uma maneira de expressar "a lista de valores de matriz". Algo que o analisador de shell entende. Semelhante à maneira a:

$ echo "$@"

expande para toda a lista "Parâmetros posicionais".

Isaac
fonte
2

Embora as matrizes indexadas ao número inteiro do bash possam ser definidas e acessadas iterativamente da mesma forma;

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

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

Matrizes indexadas associativas ou baseadas em strings no bash requerem a seguinte definição iterativa;

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

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

Para responder à pergunta sobre ponteiros e usar um do bash; a funcionalidade interna do binário bash compilado de fato faz uso de ponteiros para a memória alocada na pilha e expõe funcionalidade semelhante com o uso de eval. Veja [referências indiretas] http://tldp.org/LDP/abs/html/ivr.html )

Existem dragões; o uso de evaldeve ser usado com cautela devido a implicações de segurança

jas-
fonte