Czy bash zapewnia obsługę wskaźników?

12

Proste pytanie. Czy powłoka bash ma jakieś wsparcie dla używania wskaźników podczas pisania skryptu powłoki?

Znam notację ekspansji ${var[@]}podczas iteracji po tablicy $var, ale nie jest jasne, czy używa wskaźników do iteracji po indeksach tablicy. Czy bash zapewnia dostęp do adresów pamięci, podobnie jak inne języki?

Jeśli bash nie obsługuje używania wskaźników, to co robią inne powłoki?

111 ---
źródło

Odpowiedzi:

28

Wskaźnik (do lokalizacji pamięci ) nie jest tak naprawdę użyteczną koncepcją na żadnym poziomie wyższym niż C, czy jest to coś takiego jak Python lub powłoka. Odniesienia do obiektów są oczywiście przydatne w językach wysokiego poziomu, być może nawet konieczne do budowania złożonych struktur danych. Ale w większości przypadków myślenie w kategoriach adresów pamięci jest zbyt niskie, aby było bardzo przydatne.

W Bash (i innych powłokach) możesz uzyskać wartości elementów tablicy za pomocą ${array[index]}notacji, przypisać je za pomocą array[index]=...i uzyskać liczbę elementów w tablicy za pomocą ${#array[@]}. Wyrażenie w nawiasach jest wyrażeniem arytmetycznym. Jako gotowy przykład możemy dodać stały prefiks do wszystkich elementów tablicy:

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

(Gdybyśmy dbali tylko o wartości, a nie o indeksy, for x in "${array[@]}" ; do...byłoby dobrze.)

Z asocjacyjnych lub rozrzedzony tablice , pętla numeryczna nie ma większego sensu, ale zamiast tego, że musimy sprowadzić tablicy kluczy / z indeksów ${!array[@]}. Na przykład

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

Oprócz tego Bash ma dwa sposoby na pośrednie wskazanie innej zmiennej:

  • Ekspansja pośrednie , korzystając z ${!var}składni , który używa wartości zmiennej, której nazwa jest var, i
  • namerefs , które należy utworzyć za pomocą declarewbudowanego (lub kshkompatybilnego synonimu typeset). declare -n ref=vartworzy refodniesienie do zmiennej var.

Namerefs również wsparcie indeksowania, że jeśli mamy arr=(a b c); declare -n ref=arr;wtedy ${ref[1]}rozwinie się b. Użycie ${!p[1]}wziąłoby zamiast tego pjako tablicę i odnosi się do zmiennej nazwanej przez jej drugi element.

W Bash nameref to dosłownie to, że odwołania według nazwy i użycie nameref z wnętrza funkcji użyje lokalnej wartości nazwanej zmiennej. To zostanie wydrukowane local value of var.

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

BashFAQ ma również dłuższy artykuł na temat pośrednictwa .

ilkkachu
źródło
2
pośrednie jest dość przydatne w językach wyższego poziomu. np. odniesienia w perlu. Nie są takie same jak wskaźniki C, ale pełnią tę samą podstawową funkcję. Nawet bash może uzyskać dostęp do zmiennych pośrednio ... ale IMO, jeśli zaczniesz pisać kod, który w znaczący sposób korzysta z tej funkcji, lepiej zacząć od zera za pomocą perla lub czegoś takiego. Zobacz także mywiki.wooledge.org/BashFAQ/006
cas
2
@cas, oh, absolutnie. Ale prawdopodobnie lepiej jest myśleć o nich jako o obiektach niż o adresach pamięci. (Nawet w C jest pewien typ.) Chciałem zwrócić uwagę zarówno na rozwinięcie pośrednie, jak i na nazwy, ale nie miałem czasu, aby zrobić to natychmiast.
ilkkachu
Prawdopodobnie warto zauważyć, że przykład dla pętli for jest napisany bardziej naturalnie, for foo in "${array[@]}" ; do ... donechyba że potrzebujesz indeksu do innych celów.
Will Crawford,
@WillCrawford, punkt. zredagował przykład i zanotował.
ilkkachu
9

Nie, bashnie ma „wskaźników”, ale ma odniesienia:

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

Ze strony podręcznika bash:

Zmiennej można przypisać atrybut nameref przy użyciu opcji -n do komend declarelub localwbudowanych poleceń, aby utworzyć nazwę lub odwołanie do innej zmiennej. Pozwala to na manipulowanie zmiennymi pośrednio. Ilekroć zmienna nameref jest przywoływana, przypisywana, usuwana lub modyfikowana jest jej atrybuty (inne niż używanie lub zmiana samego atrybutu nameref), operacja jest faktycznie wykonywana na zmiennej określonej przez wartość zmiennej nameref. Nazeref jest powszechnie używany w funkcjach powłoki w celu odniesienia do zmiennej, której nazwa jest przekazywana jako argument do funkcji. Na przykład, jeśli nazwa zmiennej zostanie przekazana do funkcji powłoki jako pierwszy argument, uruchomiona

declare -n ref=$1

wewnątrz funkcji tworzy zmienną nameref ref, której wartością jest nazwa zmiennej przekazywana jako pierwszy argument. Odwołania i przypisania do odwołania oraz zmiany jego atrybutów są traktowane jako odniesienia, przypisania i modyfikacje atrybutów zmiennej, której nazwa została przekazana jako 1 $. Jeśli zmienna kontrolna w pętli for ma atrybut nameref, lista słów może być listą zmiennych powłoki, a odniesienie nazwy zostanie ustanowione dla każdego słowa na liście, gdy pętla zostanie wykonana. Zmienne tablicowe nie mogą otrzymać atrybutu nameref. Jednak zmienne nameref mogą odwoływać się do zmiennych tablicowych i zmiennych tablic dolnych. Namerefs można rozbroić za pomocą opcji -n unsetwbudowanego. W przeciwnym razie, jeśliunset jest wykonywany z nazwą zmiennej nameref jako argumentem, zmienna, do której odwołuje się zmienna nameref, zostanie rozbrojona.

hackerb9
źródło
4

Nie, powłoki nie używają „wskaźników” (w rozumieniu C).

Tablice mogą korzystać z indeksów: echo "${array[2]}"ale @w twoim przykładzie tak naprawdę nie jest to „wskaźnik”. Jest to sposób wyrażenia „listy wartości tablic”. Coś, co parser powłoki rozumie. Podobnie do sposobu:

$ echo "$@"

rozwija się do wszystkich list „Parametry pozycyjne”.

NotAnUnixNazi
źródło
2

Podczas gdy tablice indeksowane liczbami całkowitymi bash mogą być definiowane i dostępne iteracyjnie tak;

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

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

Tablice indeksowane asocjacyjne lub oparte na łańcuchach w bash wymagają następującej iteracyjnej definicji;

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

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

Aby odpowiedzieć na pytanie dotyczące wskaźników i korzystania z jednego z bash; wewnętrzna funkcjonalność skompilowanego pliku bash naprawdę korzysta ze wskaźników do pamięci przydzielonej na stosie i udostępnia podobną funkcjonalność przy użyciu eval. Zobacz [odniesienia pośrednie] http://tldp.org/LDP/abs/html/ivr.html )

Są smoki; Stosowanie evalnależy stosować ostrożnie ze względu na wpływ na bezpieczeństwo

jas-
źródło