Bash dizisi (array), tek bir değişken adı altında sıralı birden çok değer tutmanızı sağlayan yapıdır. meyveler=(elma armut "kara dut") ile tanımlar, ${meyveler[0]} ile tek bir elemana, "${meyveler[@]}" ile bütün elemanlara ulaşırsınız. Eleman sayısını ${#meyveler[@]}, kullanılan indisleri ${!meyveler[@]} verir; sona eleman eklemek için meyveler+=(ayva), bir elemanı silmek için unset 'meyveler[1]' yazarsınız.
Bu yazı Bash kılavuzunun 6.7 Arrays bölümündeki indeksli dizileri anlatıyor: tanımlama biçimleri, boşluklu elemanları bozmadan dolaşma, seyrek (aralıklı) diziler, dilimleme, eleman silme ve en sık yapılan hatalar. Anahtarı metin olan diziler ayrı bir konu; onlar için ilişkisel dizi yazısına bakın. Serinin tamamı Bash öğrenme rehberinde duruyor.
Bash dizisi nedir, ne zaman gerekir?

Bash iki tür dizi tanır: sayısal indeksli diziler ve ilişkisel diziler. İkisi de tek boyutludur; dizi içinde dizi tutamazsınız. İndeksli dizide indisler sıfırdan başlar ve aritmetik ifade olarak değerlendirilir, yani ${a[i+1]} gibi bir yazım geçerlidir. Kılavuza göre dizinin bir boyut sınırı yoktur ve indislerin ardışık olması da gerekmez: 0, 3 ve 100 numaralı elemanları olan bir dizi tamamen geçerlidir.
Diziye en çok, içinde boşluk bulunabilecek bir değer listesini güvenle taşımanız gerektiğinde ihtiyaç duyarsınız. Dosya adlarını tek bir metin değişkeninde boşlukla ayırarak tutmak, "Yeni Klasör" gibi bir ad geldiğinde listeyi ikiye böler. Dizide ise her eleman kendi sınırına sahiptir ve doğru tırnaklamayla hiçbir zaman bölünmez. Bir komuta verilecek seçenekleri adım adım biriktirmek de dizinin klasik kullanım alanıdır.
Dizi tanımlama biçimleri
En yaygın yol bileşik atamadır (compound assignment): parantez içine boşlukla ayrılmış değerler yazarsınız. Her değer normal kabuk genişletmelerinden geçer, bu yüzden boşluk içeren bir değeri tırnak içine almanız gerekir. İsterseniz [indis]=değer biçimiyle belirli bir indise de yazabilirsiniz; indis verilmeyen bir sonraki değer, son atanan indisin bir fazlasına yerleşir.
meyveler=(elma armut "kara dut")
declare -p meyveler
# çıktı: declare -a meyveler=([0]="elma" [1]="armut" [2]="kara dut")
# indisli bileşik atama: 5 yazıldıktan sonra gelen değer 6 olur
gunler=(pzt [5]=cmt paz)
declare -p gunler
# çıktı: declare -a gunler=([0]="pzt" [5]="cmt" [6]="paz")
# tek eleman atamak diziyi kendiliğinden oluşturur
renkler[2]=mavi
declare -p renkler
# çıktı: declare -a renkler=([2]="mavi")declare -p değişkeni yeniden kullanılabilir biçimde yazdırdığı için diziyi incelemenin en güvenilir yoludur; hangi indiste ne olduğunu açıkça gösterir. Diziyi önceden açıkça bildirmek isterseniz declare -a ad yazarsınız. Kılavuz declare -a ad[5] biçimini de kabul ettiğini ama köşeli parantezdeki indisin yok sayıldığını belirtiyor. Bildirim ve nitelikler için declare ve readonly yazısına bakabilirsiniz.
Eşittir işaretinin iki yanında boşluk olmamalıdır; meyveler = (elma) yazarsanız Bash meyveler adlı bir komut çalıştırmaya çalışır. Bu kural normal değişkenlerle aynıdır, ayrıntısı değişkenler yazısında.
Elemanlara erişim ve negatif indis
Bir elemanı okumak için süslü parantez şarttır: ${meyveler[1]}. Parantezi unutup $meyveler[1] yazarsanız Bash önce $meyveler değerini, yani ilk elemanı genişletir ve arkasına düz metin olarak [1] ekler. Kılavuz bu zorunluluğun nedenini köşeli parantezin dosya adı genişletmesindeki anlamıyla çakışmayı önlemek olarak açıklar.
Negatif indis dizinin sonundan geriye doğru sayar: -1 son elemanı, -2 sondan bir öncekini verir. Burada "son", en büyük indise sahip eleman demektir; dizi seyrekse bu önemli bir ayrıntıdır. İndissiz başvuru, yani $meyveler, her zaman ${meyveler[0]} ile aynıdır.
meyveler=(elma armut "kara dut")
echo "${meyveler[1]}"
# çıktı: armut
echo "${meyveler[-1]}"
# çıktı: kara dut
echo "$meyveler[1]" # süslü parantez yok: ilk eleman + "[1]"
# çıktı: elma[1]
echo "${meyveler[5]-tanımsız}"
# çıktı: tanımsız
i=0
echo "${meyveler[i+2]}" # indis aritmetik ifadedir, $ gerekmez
# çıktı: kara dutVar olmayan bir indisi okumak hata vermez, boş metin döner. Yukarıdaki ${meyveler[5]-tanımsız} yazımı bu durumda varsayılan değer verir; bu operatörlerin tamamı varsayılan değer yazısında anlatılıyor. Negatif indisle yazma da mümkündür, ancak sonuç dizinin başından öteye düşerse Bash bad array subscript hatası verir.
Tüm elemanlar: "${dizi[@]}" ile "${dizi[*]}" farkı
@ ve * indisleri dizinin tüm elemanlarını verir. Tırnak dışında aralarında fark yoktur; ikisi de genişledikten sonra kelime bölünmesine ve dosya adı genişletmesine uğrar. Fark çift tırnak içinde ortaya çıkar: "${dizi[@]}" her elemanı ayrı bir kelime olarak verir, "${dizi[*]}" ise bütün elemanları IFS değişkeninin ilk karakteriyle birleştirip tek bir kelime üretir. Bu davranış betiğe gelen argümanlardaki "$@" ve "$*" farkının aynısıdır; o taraf için özel değişkenler yazısına bakın.
meyveler=(elma armut "kara dut")
for m in "${meyveler[@]}"; do echo "[$m]"; done
# çıktı:
# [elma]
# [armut]
# [kara dut]
for m in ${meyveler[@]}; do echo "[$m]"; done # tırnaksız: bölünür
# çıktı:
# [elma]
# [armut]
# [kara]
# [dut]
IFS=,
echo "${meyveler[*]}" # IFS ilk karakteriyle birleştirir
# çıktı: elma,armut,kara dut
unset IFSPratik kural basittir: elemanları bir döngüye veya komuta aktarırken her zaman "${dizi[@]}" kullanın; elemanları tek bir metne birleştirmek istediğinizde "${dizi[*]}" kullanın. IFS değerini değiştirdiyseniz işiniz bitince unset IFS ile varsayılan davranışa dönün ya da değişikliği bir fonksiyonun içinde local IFS=, ile sınırlayın. Kelime bölünmesinin nasıl çalıştığı tırnak kullanımı yazısında ayrıca ele alınıyor. Dizi döngülerinin diğer biçimleri için for döngüsü yazısına bakabilirsiniz.
Eleman sayısı, eleman uzunluğu ve indis listesi

Üç genişletme birbirine çok benzer ama farklı şeyler döndürür. ${#dizi[@]} dizideki eleman sayısını verir. ${#dizi[2]} 2 numaralı elemanın karakter cinsinden uzunluğunu verir. ${!dizi[@]} ise atanmış indislerin listesini döndürür. Dikkat: ${#dizi} yazarsanız indissiz başvuru kuralı devreye girer ve ilk elemanın uzunluğunu alırsınız, eleman sayısını değil.
seyrek=([0]=a [3]=b [7]=c)
echo "${#seyrek[@]}" # eleman sayısı
# çıktı: 3
echo "${!seyrek[@]}" # atanmış indisler
# çıktı: 0 3 7
adlar=(Ayşe "Gülşen Yılmaz")
echo "${#adlar[1]}" # 1 numaralı elemanın uzunluğu
# çıktı: 13
echo "${#adlar}" # dikkat: ilk elemanın uzunluğu
# çıktı: 4Seyrek dizilerde eleman sayısı en büyük indisin bir fazlası değildir. Yukarıdaki dizide en büyük indis 7 olduğu halde eleman sayısı 3. Bu yüzden for ((i=0; i<${#dizi[@]}; i++)) biçimindeki C tarzı döngü, arada boşluk olan dizilerde bazı elemanları atlar ve boş indisleri okur. İndisler sizin için önemliyse for i in "${!dizi[@]}" ile gerçekten var olan indisleri dolaşın. Türkçe harflerin doğru sayılması için betiğin UTF-8 bir yerel ayarla çalışması gerekir; yerel ayar karakter yerine bayt saydığında "Gülşen Yılmaz" daha uzun görünür.
Diziye eleman ekleme: += operatörü
Var olan bir dizinin sonuna eleman eklemek için += ile bileşik atama yaparsınız: dizi+=(yeni "başka değer"). Kılavuza göre bu durumda dizi sıfırlanmaz ve yeni değerler en büyük indisin bir fazlasından itibaren yerleştirilir. Seyrek bir dizide de ekleme her zaman en sona yapılır, aradaki boş indisler doldurulmaz.
liste=(bir iki)
liste+=(üç "dört beş")
declare -p liste
# çıktı: declare -a liste=([0]="bir" [1]="iki" [2]="üç" [3]="dört beş")
# yaygın hata: parantezsiz += ilk elemana metin ekler
hata=(bir iki)
hata+=üç
declare -p hata
# çıktı: declare -a hata=([0]="birüç" [1]="iki")
# indis vererek tek eleman eklemek de mümkün
liste[${#liste[@]}]=altı
echo "${liste[-1]}"
# çıktı: altıParantezsiz hata+=üç bir dizi ekleme değildir; indissiz başvuru 0 numaralı elemana gittiği için metin birleştirme yapar. Bu hata hiçbir uyarı üretmediği için fark edilmesi zordur. Son satırdaki liste[${#liste[@]}]=değer kalıbı yalnızca dizi ardışıksa doğru çalışır; seyrek dizide eleman sayısı en büyük indisten küçük olabileceği için var olan bir elemanın üzerine yazabilir. Bu yüzden +=( ) biçimi her durumda daha güvenlidir.
Dilimleme: ${dizi[@]:başlangıç:uzunluk}
Alt metin genişletmesinin dizi biçimi, belirli bir noktadan başlayan elemanları verir. ${dizi[@]:2} 2 numaralı elemandan sonuna kadar, ${dizi[@]:1:2} 1 numaradan başlayarak iki eleman döndürür. Negatif başlangıç sondan sayar, ancak eksi işaretini iki noktadan bir boşlukla ayırmanız gerekir; yoksa Bash :- varsayılan değer operatörünü görür. Uzunluk negatif olursa dizilerde bu bir genişletme hatasıdır.
harf=(a b c d e f)
echo "${harf[@]:2}"
# çıktı: c d e f
echo "${harf[@]:1:2}"
# çıktı: b c
echo "${harf[@]: -2}" # boşluk şart
# çıktı: e f
# seyrek dizide başlangıç bir indis değeridir, sıra numarası değil
s=([0]=a [3]=b [7]=c [8]=d)
echo "${s[@]:4:2}"
# çıktı: c dSon satır seyrek dizilerdeki ince noktayı gösteriyor: başlangıç değeri "kaçıncı eleman" anlamına gelmez, kılavuzun ifadesiyle ${dizi[başlangıç]} konumundan itibaren gelen elemanları seçer. 4 numaralı indis boş olduğu için dilim ilk dolu indisten, 7'den başlar. Dilimlemenin metin değişkenlerindeki karşılığı, yani ${metin:2:3}, string işlemleri yazısında anlatılıyor.
Eleman silme ve diziyi yeniden numaralama
Bir elemanı silmek için unset 'dizi[indis]' yazarsınız. Tek tırnak keyfi değildir: kılavuz, unset a[4] gibi tırnaksız bir argümanın dosya adı genişletmesine uğrayacağını ve çalışma klasöründe a4 adlı bir dosya varsa komutun o adı alacağını hatırlatır. Silme işlemi indisleri kaydırmaz; dizi seyrek hale gelir. Bütün diziyi kaldırmak için unset dizi yeterlidir.
harf=(a b c d)
unset 'harf[1]'
declare -p harf
# çıktı: declare -a harf=([0]="a" [2]="c" [3]="d")
unset 'harf[-1]' # negatif indis burada da geçerli
declare -p harf
# çıktı: declare -a harf=([0]="a" [2]="c")
# boşlukları kapatmak için yeniden atayın
harf=("${harf[@]}")
declare -p harf
# çıktı: declare -a harf=([0]="a" [1]="c")
unset 'harf[@]' # tüm elemanlar gider, değişken kalır
declare -p harf
# çıktı: declare -a harf=()harf=("${harf[@]}") kalıbı elemanları sırasıyla yeni bir diziye kopyalar ve indisleri 0'dan yeniden verir. unset 'harf[@]' davranışı sürüme bağlıdır: kılavuzun uyumluluk modu bölümü, Bash 5.2'nin indeksli dizide bu komutla tüm elemanları silip diziyi yerinde bıraktığını, uyumluluk seviyesi 51 veya altına ayarlandığında ise dizinin tamamen kaldırıldığını söylüyor. Son elemanı silmek de diziyi yok etmez; boş bir dizi değişken olarak kalmaya devam eder.
Tüm elemanlara aynı anda işlem uygulama
Metin genişletmelerinin çoğu [@] ile kullanıldığında her elemana tek tek uygulanır ve sonuç yine bir liste olur. Böylece döngü yazmadan bütün elemanlardan bir ön eki kaldırabilir, uzantı değiştirebilir veya harfleri büyütebilirsiniz. Kılavuz bunu desen silme (#, %), desen değiştirme (/), büyük/küçük harf dönüşümü ve @ operatörleri için ayrı ayrı belirtir.
dosyalar=(rapor.txt notlar.txt "yıllık özet.txt")
printf '%s\n' "${dosyalar[@]/%.txt/.md}"
# çıktı:
# rapor.md
# notlar.md
# yıllık özet.md
printf '%s\n' "${dosyalar[@]%.txt}"
# çıktı:
# rapor
# notlar
# yıllık özet
echo "${dosyalar[@]@Q}" # her elemanı yeniden kullanılabilir biçimde tırnaklar
# çıktı: 'rapor.txt' 'notlar.txt' 'yıllık özet.txt'printf '%s\n' biçim dizgesini her argüman için yeniden kullandığından, dizinin her elemanını ayrı bir satıra yazdırmanın en temiz yoludur; ayrıntısı echo ve printf yazısında. @Q dönüşümü hata ayıklarken elemanların gerçekte nerede başlayıp bittiğini görmenizi sağlar.
Komut çıktısını diziye almak
Bir komutun çıktısını dizi=($(komut)) ile diziye almak çok yaygın ama kırılgan bir alışkanlıktır. Komut ikamesinin sonucu tırnaksız olduğu için kelime bölünmesine ve dosya adı genişletmesine uğrar: boşluklu satırlar parçalanır, çıktıda * geçiyorsa o karakter bulunduğunuz klasördeki dosya adlarına dönüşebilir. Satır satır okumak istiyorsanız bunun yerine mapfile -t dizi < <(komut) kullanın; mapfile ve readarray yazısı bu konuyu ayrıntılı işliyor. Tek bir satırı kelimelere bölüp diziye almak için read -r -a uygundur; seçenekleri read yazısında bulabilirsiniz.
satirlar='ilk satır
ikinci satır'
kirik=($(printf '%s\n' "$satirlar"))
echo "${#kirik[@]}" # her kelime ayrı eleman oldu
# çıktı: 4
mapfile -t dogru < <(printf '%s\n' "$satirlar")
echo "${#dogru[@]}" # her satır bir eleman
# çıktı: 2
read -r -a kelimeler <<< "elma armut ayva"
echo "${kelimeler[2]}"
# çıktı: ayvaUygulama: komut argümanlarını dizide biriktirmek
Dizilerin en faydalı kullanımlarından biri, bir komuta verilecek argümanları koşullara göre adım adım toplamaktır. Argümanları tek bir metin değişkeninde biriktirip tırnaksız genişletirseniz, boşluk içeren bir dosya adı iki ayrı argümana dönüşür. Diziyle bu sorun ortadan kalkar. Aşağıdaki örnekte goster fonksiyonu kendisine gelen argümanları köşeli parantez içinde yazdırarak komutun gerçekte ne alacağını gösteriyor.
goster() { echo "$# argüman:"; printf ' [%s]\n' "$@"; }
ayrintili=evet
hedef="Yedek Klasörü"
argumanlar=(-r)
if [[ $ayrintili == evet ]]; then
argumanlar+=(--verbose)
fi
argumanlar+=(--hedef "$hedef")
goster "${argumanlar[@]}"
# çıktı:
# 4 argüman:
# [-r]
# [--verbose]
# [--hedef]
# [Yedek Klasörü]
# aynı işi metin değişkeniyle yapmak argümanı böler
metin="-r --hedef $hedef"
goster $metin
# çıktı:
# 4 argüman:
# [-r]
# [--hedef]
# [Yedek]
# [Klasörü]Dizi yöntemiyle "Yedek Klasörü" tek bir argüman olarak kalıyor, metin yönteminde ise ikiye bölünüyor. Gerçek bir betikte goster yerine çalıştırmak istediğiniz komutu yazarsınız: rsync "${argumanlar[@]}" kaynak/ hedef/ gibi. Diziyi bir fonksiyona aktarmanız gerektiğinde en sade yol elemanları "${dizi[@]}" ile argüman olarak geçmektir; fonksiyonun diziyi adıyla değiştirmesi gerekiyorsa declare -n ile isim referansı kullanılır, bu teknik local ve kapsam yazısında anlatılıyor.
Hızlı başvuru tablosu
![Hızlı başvuru tablosu: a=(x y "z w"), a[5]=v, ${a[i]}, ${a[-1]}](/assets/blog/bash-dizi-array-kullanimi-b12.webp)
| Yazım | Ne verir |
|---|---|
| a=(x y "z w") | üç elemanlı dizi tanımlar |
| a[5]=v | 5 numaralı indise yazar, gerekirse diziyi oluşturur |
| ${a[i]} | i numaralı eleman (i aritmetik ifade) |
| ${a[-1]} | en büyük indisteki eleman |
| "${a[@]}" | her eleman ayrı kelime |
| "${a[*]}" | tüm elemanlar IFS ilk karakteriyle birleşik tek kelime |
| ${#a[@]} | eleman sayısı |
| ${#a[i]} | i numaralı elemanın uzunluğu |
| ${!a[@]} | atanmış indislerin listesi |
| a+=(v) | sona eleman ekler |
| ${a[@]:s:n} | s indisinden başlayarak n eleman |
| unset 'a[i]' | i numaralı elemanı siler, indisler kaymaz |
| a=("${a[@]}") | indisleri 0dan yeniden numaralar |
Sık yapılan hatalar
- Tırnaksız
${dizi[@]}: boşluklu elemanlar bölünür, desen karakterleri dosya adlarına dönüşebilir. Her zaman çift tırnak kullanın. $dizi[1]yazmak: süslü parantez olmadan ilk elemanın değeri ve düz metin olarak[1]gelir.${#dizi}ile eleman saymak: bu ifade ilk elemanın uzunluğudur; eleman sayısı için${#dizi[@]}yazın.- Parantezsiz
dizi+=değer: eleman eklemez, 0 numaralı elemanın sonuna metin ekler. unset dizi[1]yazmak: tırnaksız argüman dosya adı genişletmesine uğrayabilir;unset 'dizi[1]'yazın.- Silmeden sonra indislerin kaydığını sanmak: silinen indis boş kalır; gerekiyorsa diziyi yeniden atayarak numaralayın.
- Seyrek dizide C tarzı döngü:
${#dizi[@]}en büyük indisi değil eleman sayısını verir; indisleri"${!dizi[@]}"ile dolaşın.
Sık Sorulan Sorular
Bash dizisinin boyut sınırı var mı?
Kılavuza göre yoktur. Pratikte sınırı sistemin belleği belirler. Ancak çok büyük veriyi kabuk dizisinde işlemek yavaş olabilir; bu tür işler için satır satır akış işleyen araçlar daha uygundur.
Bir değerin dizide olup olmadığını nasıl kontrol ederim?
İndeksli dizide yerleşik bir "içerir" işlemi yoktur; elemanları bir döngüyle dolaşıp [[ $eleman == "$aranan" ]] ile karşılaştırırsınız. Bu tür aramaları sık yapıyorsanız değeri anahtar olarak kullanan bir ilişkisel dizi daha uygun olur; [[ -v 'kume[elma]' ]] ile tek adımda kontrol edebilirsiniz.
Dizi çok boyutlu olabilir mi?
Hayır, Bash dizileri tek boyutludur. İki boyutlu bir tabloyu taklit etmek için ilişkisel dizide [satır,sütun] gibi birleşik anahtarlar kullanılabilir, ama bu yalnızca bir adlandırma kuralıdır.
Dizi /bin/sh betiklerinde çalışır mı?
POSIX sh dizileri tanımlamaz; bu yazıdaki sözdizimi Bash'e özgüdür. Betiğinizin ilk satırının Bash'i gösterdiğinden emin olun. Farkların listesi için Bash ve sh farkı yazısına bakabilirsiniz.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Bash öğrenme rehberi · İlişkisel diziler · mapfile ile dosyayı diziye okuma · declare ve readonly · for döngüsü · String işlemleri