Bietet bash Unterstützung für die Verwendung von Zeigern?

12

Einfache Frage. Hat die Bash-Shell Unterstützung für die Verwendung von Zeigern beim Schreiben eines Shell-Skripts?

Ich bin mit der Erweiterungsnotation vertraut, ${var[@]}wenn ich über das Array iteriere $var, aber es ist nicht klar, dass hier Zeiger verwendet werden, um über die Array-Indizes zu iterieren. Bietet bash Zugriff auf Speicheradressen wie andere Sprachen?

Wenn bash die Verwendung von Zeigern nicht unterstützt, welche anderen Shells tun dies?

111 ---
quelle

Antworten:

27

Ein Zeiger (auf einen Speicherort ) ist in keiner höheren Ebene als C ein wirklich nützliches Konzept, sei es in etwa Python oder der Shell. Verweise auf Objekte sind natürlich in Hochsprachen nützlich, möglicherweise sogar zum Aufbau komplexer Datenstrukturen erforderlich. In den meisten Fällen ist das Denken in Speicheradressen jedoch zu niedrig, um sehr nützlich zu sein.

In Bash (und anderen Shells) können Sie die Werte von Array-Elementen mit der ${array[index]}Notation abrufen, sie mit zuweisen array[index]=...und die Anzahl der Elemente im Array mit abrufen ${#array[@]}. Der Ausdruck in Klammern ist ein arithmetischer Ausdruck. Als erfundenes Beispiel könnten wir allen Array-Mitgliedern ein konstantes Präfix hinzufügen:

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

(Wenn wir uns nur um die Werte und nicht um die Indizes kümmern for x in "${array[@]}" ; do...würden , wäre das in Ordnung.)

Bei assoziativen oder spärlichen Arrays macht eine numerische Schleife nicht viel Sinn, sondern wir müssten die Array-Schlüssel / -Indizes mit holen ${!array[@]}. Z.B

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

Darüber hinaus hat Bash zwei Möglichkeiten, indirekt auf eine andere Variable zu verweisen:

  • indirekte Erweiterung mit der ${!var}Syntax , die den Wert der Variablen verwendet, deren Name in varund
  • namerefs , die mit dem declareeingebauten (oder dem kshkompatiblen Synonym typeset) erstellt werden müssen. declare -n ref=varnimmt refauf die Variable Bezug var.

Namerefs auch Unterstützung Indexierung, dass , wenn wir arr=(a b c); declare -n ref=arr;dann ${ref[1]}expandieren b. Verwenden ${!p[1]}würde stattdessen pals Array nehmen und auf die Variable verweisen, die von ihrem zweiten Element benannt wird.

In Bash sind namerefs wörtlich: Verweise nach Namen und die Verwendung eines nameref aus einer Funktion verwenden den lokalen Wert der benannten Variablen. Dies wird gedruckt local value of var.

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

BashFAQ hat auch einen längeren Artikel zum Thema Indirektion .

ilkkachu
quelle
2
Indirektion ist in höheren Sprachen sehr nützlich. zB Referenzen in Perl. Sie sind nicht mit C-Zeigern identisch, haben jedoch die gleiche Grundfunktion. Sogar bash kann indirekt auf Variablen zugreifen ... aber IMO, wenn Sie anfangen, Code zu schreiben, der die Funktion in erheblichem Maße nutzt, ist es besser, mit Perl oder ähnlichem von vorne zu beginnen. Siehe auch mywiki.wooledge.org/BashFAQ/006
cas
2
@cas, oh, absolut. Aber es ist wahrscheinlich besser, sich vorzustellen, dass sie auf Objekte zeigen , als auf Speicheradressen. (Sogar in C ist ein Typ involviert.) Ich wollte sowohl indirekte Expansion als auch namerefs notieren, hatte aber nicht die Zeit, es sofort zu tun.
ilkkachu
Es ist wahrscheinlich erwähnenswert, dass das for-loop-Beispiel natürlicher geschrieben ist, es for foo in "${array[@]}" ; do ... donesei denn, Sie benötigen den Index für andere Zwecke.
Will Crawford
@ WillCrawford, Punkt. bearbeitet das Beispiel und notiert.
Ilkkachu
9

Nein, bashhat keine "Zeiger", aber Referenzen:

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

Von der bashManpage:

Einer Variablen kann das Attribut nameref mit der Option -n für die Befehle declareoder localbuiltin zugewiesen werden, um eine Namensreferenz oder einen Verweis auf eine andere Variable zu erstellen. Dies ermöglicht die indirekte Manipulation von Variablen. Immer wenn die nameref-Variable referenziert, zugewiesen, nicht festgelegt oder in ihren Attributen geändert wird (außer das nameref-Attribut selbst zu verwenden oder zu ändern), wird der Vorgang tatsächlich für die durch den Wert der nameref-Variablen angegebene Variable ausgeführt. Ein nameref wird häufig in Shell-Funktionen verwendet, um auf eine Variable zu verweisen, deren Name als Argument an die Funktion übergeben wird. Wenn beispielsweise ein Variablenname als erstes Argument an eine Shell-Funktion übergeben wird, wird ausgeführt

declare -n ref=$1

Innerhalb der Funktion wird eine nameref-Variable ref erstellt, deren Wert der Variablenname ist, der als erstes Argument übergeben wird. Verweise und Zuweisungen an ref sowie Änderungen an den Attributen werden als Verweise, Zuweisungen und Attributänderungen an der Variablen behandelt, deren Name als $ 1 übergeben wurde. Wenn die Steuervariable in einer for-Schleife das Attribut nameref hat, kann die Liste der Wörter eine Liste der Shell-Variablen sein, und für jedes Wort in der Liste wird nacheinander eine Namensreferenz erstellt, wenn die Schleife ausgeführt wird. Array-Variablen können nicht mit dem Attribut nameref versehen werden. Nameref-Variablen können jedoch auf Array-Variablen und tiefgestellte Array-Variablen verweisen. Namerefs können mit der Option -n auf den unseteingebauten Wert deaktiviert werden. Ansonsten, wennunset Wird mit dem Namen einer nameref-Variablen als Argument ausgeführt, wird die Variable, auf die die nameref-Variable verweist, nicht gesetzt.

hackerb9
quelle
4

Nein, Shells verwenden keine "Zeiger" (wie in C verstanden).

Arrays könnten Indizes verwenden: echo "${array[2]}"aber das @in Ihrem Beispiel ist nicht wirklich ein "Zeiger". Es ist eine Möglichkeit, "die Liste der Array-Werte" auszudrücken. Etwas, das der Shell-Parser versteht. Ähnlich wie bei a:

$ echo "$@"

wird auf alle "Positionsparameter" -Listen erweitert.

Isaac
quelle
2

Während Bash Integer indizierte Arrays definiert werden können und iterativ wie folgt zugegriffen werden kann;

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

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

Assoziative oder stringbasierte indizierte Arrays in bash erfordern die folgende iterative Definition.

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

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

Um die Frage nach Zeigern zu beantworten und einen von bash zu verwenden; Die interne Funktionalität der kompilierten Bash-Binärdatei verwendet in der Tat Zeiger auf den auf dem Stapel zugewiesenen Speicher und stellt eine ähnliche Funktionalität unter Verwendung von bereit eval. Siehe [indirekte Verweise] http://tldp.org/LDP/abs/html/ivr.html )

Es gibt Drachen; Die Verwendung von evalsollte aus Sicherheitsgründen mit Vorsicht erfolgen

jas-
quelle