İlişkilendirilebilir dizilerin ötesinde, Bash'te dinamik değişkenlere ulaşmanın birkaç yolu vardır. Tüm bu tekniklerin, bu cevabın sonunda tartışılan riskler sunduğunu unutmayın.
Aşağıdaki örneklerde , ilk değeri olan i=37
değişkenin diğer adını taklit etmek istediğinizi varsayacağım .var_37
lolilol
Yöntem 1. “İşaretçi” değişkeni kullanma
Değişkenin adını basitçe bir C işaretçisinin aksine bir dolaylı değişkente saklayabilirsiniz. Bash daha sonra, diğer değişkenin okunması için bir sözdizimine sahiptir : adı değişkenin ${!name}
değeri olan değişkenin değerine genişler name
. Bunu iki aşamalı bir genişleme olarak düşünebilirsiniz: ${!name}
genişler $var_37
, genişler lolilol
.
name="var_$i"
echo "$name" # outputs “var_37”
echo "${!name}" # outputs “lolilol”
echo "${!name%lol}" # outputs “loli”
# etc.
Maalesef, diğer adı değiştirilen değişkeni değiştirmek için bir karşılık sözdizimi yoktur . Bunun yerine, aşağıdaki numaralardan biriyle ödev alabilirsiniz.
1 A. İle atamaeval
eval
kötüdür, ama aynı zamanda hedefimize ulaşmanın en basit ve taşınabilir yoludur. İki kez değerlendirileceği için ödevin sağ tarafından dikkatlice kaçmanız gerekir . Bunu yapmanın kolay ve sistematik bir yolu, sağ tarafı önceden (veya kullanmak printf %q
) değerlendirmektir.
Ayrıca , sol tarafın geçerli bir değişken adı evil_code #
mı yoksa dizine sahip bir ad mı olduğunu kontrol etmelisiniz (ya öyleyse ?). Buna karşılık, aşağıdaki diğer tüm yöntemler otomatik olarak uygular.
# check that name is a valid variable name:
# note: this code does not support variable_name[index]
shopt -s globasciiranges
[[ "$name" == [a-zA-Z_]*([a-zA-Z_0-9]) ]] || exit
value='babibab'
eval "$name"='$value' # carefully escape the right-hand side!
echo "$var_37" # outputs “babibab”
Downsides:
- değişken adının geçerliliğini kontrol etmez.
eval
şeytan.
eval
şeytan.
eval
şeytan.
1b. İle atamaread
read
Builtin sen adını, burada-dizeleri ile birlikte yararlanılabilir olani hangi bir değişkene değerler atamanızı sağlar:
IFS= read -r -d '' "$name" <<< 'babibab'
echo "$var_37" # outputs “babibab\n”
IFS
Bölüm ve seçenek -r
olduğu gibi seçenek ise değer atanır emin olun -d ''
çok hatlı değerleri atamak için izin verir. Bu son seçenek nedeniyle, komut sıfırdan farklı bir çıkış koduyla döner.
Burada bir dize kullandığımızdan, değere bir yeni satır karakteri eklendiğini unutmayın.
Downsides:
- biraz karanlık;
- sıfır olmayan bir çıkış koduyla döner;
- değere bir satırsonu ekler.
1c. İle atamaprintf
Bash 3.1'den beri (2005 yayınlandı), printf
yerleşik ayrıca sonucunu adı verilen bir değişkene atayabilir. Önceki çözümlerin aksine, sadece işe yarıyor, şeylerden kaçmak, bölünmeyi önlemek vb. İçin ekstra çaba gerektirmiyor.
printf -v "$name" '%s' 'babibab'
echo "$var_37" # outputs “babibab”
Downsides:
- Daha az taşınabilir (ama iyi).
Yöntem 2. “Referans” değişkeni kullanma
Bash 4.3'ten (2014 yılında piyasaya sürüldüğünden) beri, declare
yerleşik -n
C ++ referansları gibi başka bir değişkene “ad referansı” olan bir değişken oluşturma seçeneğine sahiptir . Yöntem 1'deki gibi, başvuru da takma değişkenin adını saklar, ancak referansa her erişildiğinde (okuma veya atama için), Bash dolaylı olarak otomatik olarak çözer.
Buna ek olarak, Bash kendiniz referans kendisi, yargıç değerini almak için çok özel ve çok kafa karıştırıcı sözdizimi vardır: ${!ref}
.
declare -n ref="var_$i"
echo "${!ref}" # outputs “var_37”
echo "$ref" # outputs “lolilol”
ref='babibab'
echo "$var_37" # outputs “babibab”
Bu, aşağıda açıklanan tuzaklardan kaçınmaz, ancak en azından sözdizimini basitleştirir.
Downsides:
Riskler
Tüm bu takma teknikleri çeşitli riskler taşır. Birincisi, indirimi her çözdüğünüzde (okuma veya atama için) rastgele kod yürütmektir . Gerçekten de, skaler değişken adı yerine, gibi var_37
, bir dizi altyazısını da takma adlandırabilirsiniz arr[42]
. Ancak Bash, her seferinde gerektiğinde köşeli parantezlerin içeriğini değerlendirir, bu nedenle takma adınarr[$(do_evil)]
beklenmedik etkileri olur ... Sonuç olarak, bu teknikleri yalnızca takma adın provenansını kontrol ettiğinizde kullanın .
function guillemots() {
declare -n var="$1"
var="«${var}»"
}
arr=( aaa bbb ccc )
guillemots 'arr[1]' # modifies the second cell of the array, as expected
guillemots 'arr[$(date>>date.out)1]' # writes twice into date.out
# (once when expanding var, once when assigning to it)
İkinci risk, döngüsel bir takma ad oluşturmaktır. Bash değişkenleri, adlarına göre değil, adlarına göre tanımlandığından, yanlışlıkla kendi başına bir takma ad oluşturabilirsiniz (bu, onu kapsayan bir kapsamdaki bir değişkeni takma ad olarak düşünürken). Bu özellikle ortak değişken adları (gibi var
) kullanıldığında meydana gelebilir . Sonuç olarak, bu teknikleri yalnızca takma değişkenin adını kontrol ettiğinizde kullanın .
function guillemots() {
# var is intended to be local to the function,
# aliasing a variable which comes from outside
declare -n var="$1"
var="«${var}»"
}
var='lolilol'
guillemots var # Bash warnings: “var: circular name reference”
echo "$var" # outputs anything!
Kaynak: