Apakah bash menyediakan dukungan untuk menggunakan pointer?


12

Pertanyaan sederhana. Apakah shell bash memiliki dukungan untuk menggunakan pointer ketika menulis skrip shell?

Saya kenal dengan notasi ekspansi, ${var[@]}ketika iterasi di atas array $var, tetapi tidak jelas ini menggunakan pointer untuk beralih di atas indeks array. Apakah bash menyediakan akses ke alamat memori seperti bahasa lain?

Jika bash tidak mendukung penggunaan pointer, apa yang dilakukan shell lainnya?

Jawaban:


28

Pointer (ke lokasi memori ) sebenarnya bukan konsep yang berguna dalam tingkat apa pun yang lebih tinggi dari C, baik itu sesuatu seperti Python atau shell. Referensi ke objek tentu saja berguna dalam bahasa tingkat tinggi, bahkan mungkin diperlukan untuk membangun struktur data yang kompleks. Tetapi dalam kebanyakan kasus berpikir dalam hal alamat memori level terlalu rendah untuk menjadi sangat berguna.

Di Bash (dan shell lainnya), Anda bisa mendapatkan nilai elemen array dengan ${array[index]}notasi, menugaskannya, array[index]=...dan mendapatkan jumlah elemen dengan array ${#array[@]}. Ekspresi di dalam kurung adalah ekspresi aritmatika. Sebagai contoh buatan, kita bisa menambahkan awalan konstan ke semua anggota array:

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

(Jika kita hanya peduli dengan nilai, dan bukan indeks, adil for x in "${array[@]}" ; do... akan baik-baik saja.)

Dengan array asosiatif atau jarang , loop numerik tidak masuk akal, tetapi sebaliknya kita perlu mengambil kunci array / indeks dengan ${!array[@]}. Misalnya

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

Selain itu, Bash memiliki dua cara untuk menunjuk secara tidak langsung ke variabel lain:

  • ekspansi tidak langsung , menggunakan para ${!var}sintaks , yang menggunakan nilai dari variabel yang nama adalah divar , dan
  • namerefs , yang perlu dibuat dengan declarebuiltin (atau kshsinonim -compatible, typeset). declare -n ref=varmembuat refreferensi ke variabel var.

Namerefs juga mendukung pengindeksan, dalam hal ini jika kita miliki arr=(a b c); declare -n ref=arr;maka ${ref[1]}akan diperluas ke b. Menggunakan ${!p[1]}sebaliknya akan mengambilp array, dan merujuk ke variabel yang dinamai oleh elemen kedua.

Dalam Bash, nameref secara harfiah adalah itu, referensi dengan nama , dan menggunakan nameref dari dalam suatu fungsi akan menggunakan nilai lokal dari variabel bernama. Ini akan dicetak local value of var.

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

BashFAQ memiliki artikel yang lebih panjang tentang tipuan juga.


2
tipuan cukup berguna dalam bahasa tingkat yang lebih tinggi. misalnya referensi dalam perl. Mereka tidak sama dengan pointer C tetapi mereka melayani fungsi dasar yang sama. Bahkan bash dapat mengakses variabel secara tidak langsung ... tetapi IMO jika Anda mulai menulis kode yang memanfaatkan fitur secara signifikan, Anda lebih baik mulai dari awal dengan perl atau sesuatu. Lihat juga mywiki.wooledge.org/BashFAQ/006
cas

2
@cas, oh, tentu saja. Tapi mungkin lebih baik menganggap mereka sebagai menunjuk ke objek , daripada alamat memori. (Bahkan di C, ada jenis yang terlibat.) Saya bermaksud mencatat ekspansi tidak langsung dan nameref, tetapi tidak punya waktu untuk segera melakukannya.
ilkkachu

Mungkin perlu ditunjukkan bahwa contoh for-loop lebih alami ditulis for foo in "${array[@]}" ; do ... donekecuali Anda memerlukan indeks untuk tujuan lain.
Will Crawford

@ WillCrawford, titik. mengedit contoh dan mencatat.
ilkkachu

9

Tidak, bashtidak memiliki "pointer", tetapi memiliki referensi:

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

Dari bashhalaman manual:

Suatu variabel dapat diberi atribut nameref menggunakan opsi -n ke declareatau localperintah builtin untuk membuat nameref, atau referensi ke variabel lain. Ini memungkinkan variabel untuk dimanipulasi secara tidak langsung. Setiap kali variabel nameref direferensikan, ditugaskan, tidak disetel, atau atributnya diubah (selain menggunakan atau mengubah atribut nameref itu sendiri), operasi sebenarnya dilakukan pada variabel yang ditentukan oleh nilai variabel nameref. Nameref biasanya digunakan dalam fungsi shell untuk merujuk ke variabel yang namanya dilewatkan sebagai argumen ke fungsi. Misalnya, jika nama variabel dilewatkan ke fungsi shell sebagai argumen pertamanya, jalankan

declare -n ref=$1

di dalam fungsi menciptakan ref variabel nameref yang nilainya adalah nama variabel yang diteruskan sebagai argumen pertama. Referensi dan penugasan ke ref, dan perubahan pada atributnya, diperlakukan sebagai referensi, penugasan, dan modifikasi atribut ke variabel yang namanya dianggap sebagai $ 1. Jika variabel kontrol dalam loop untuk memiliki atribut nameref, daftar kata-kata bisa menjadi daftar variabel shell, dan referensi nama akan dibuat untuk setiap kata dalam daftar, pada gilirannya, ketika loop dijalankan. Variabel array tidak dapat diberikan atribut nameref. Namun, variabel nameref dapat merujuk variabel array dan variabel array yang disubkripsikan. Namerefs dapat dibatalkan menggunakan opsi -n keunset builtin. Kalau tidak, jikaunset dieksekusi dengan nama variabel nameref sebagai argumen, variabel yang direferensikan oleh variabel nameref akan tidak disetel.


4

Tidak, kerang tidak menggunakan "pointer" (sebagaimana dipahami dalam C).

Array dapat menggunakan indeks: echo "${array[2]}"tetapi @pada contoh Anda sebenarnya bukan "pointer". Ini adalah cara untuk mengekspresikan "daftar nilai array". Sesuatu yang dimengerti oleh parser shell. Mirip dengan cara a:

$ echo "$@"

memperluas ke semua daftar "Parameter Posisi".


2

Sementara bash integer array yang diindeks dapat didefinisikan & diakses secara iteratif seperti itu;

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

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

Array yang diindeks asosiatif atau berbasis string dalam bash memerlukan definisi berulang berikut ini;

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

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

Untuk menjawab pertanyaan tentang pointer dan menggunakan satu dari bash; fungsionalitas internal biner bash yang dikompilasi memang menggunakan pointer ke memori yang dialokasikan pada stack dan tidak mengekspos fungsionalitas yang sama dengan penggunaan eval. Lihat [referensi tidak langsung] http://tldp.org/LDP/abs/html/ivr.html )

Ada naga; penggunaan evalharus digunakan dengan hati-hati karena implikasi keamanan

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.