Bash'te case komutu bir kelimeyi sırayla desenlerle karşılaştırır ve ilk eşleşen desenin komutlarını çalıştırır. Yazımı case kelime in desen) komutlar ;; esac biçimindedir: her dal bir desen listesi ve kapanış parantezi ile başlar, ;; ile biter, bütün yapı esac ile kapanır. Desenler düzenli ifade değil, dosya adı genişletmesindeki *, ? ve [...] kurallarını kullanan glob desenleridir. Aynı değişkeni birçok sabit değerle karşılaştırmanız gerektiğinde uzun bir if ve elif zincirinden çok daha okunaklıdır.
Bu yazıda case sözdizimini, | ile birden fazla deseni tek dala bağlamayı, varsayılan dal için * kullanımını, Bash'e özgü ;& ve ;;& sonlandırıcılarını, tırnaklamanın desen üzerindeki etkisini ve küçük bir komut dağıtıcı örneğini göreceksiniz. Örnekler GNU Bash 5.2.37 ile çalıştırıldı.
case sözdizimi

Kılavuzdaki biçim şöyledir: case kelimesinden sonra karşılaştırılacak kelime, ardından in gelir. Sonra istediğiniz sayıda dal (kılavuzdaki adıyla clause) yazılır. Her dal, isteğe bağlı bir açılış parantezi, | ile ayrılmış bir ya da daha fazla desen, kapanış parantezi ) ve bir komut listesinden oluşur. Dal ;;, ;& ya da ;;& ile sonlandırılır. En sonda esac yazılır; bu da case kelimesinin tersten yazılışıdır.
#!/usr/bin/env bash
hayvan="kedi"
case $hayvan in
at | köpek | kedi)
echo "$hayvan dört ayaklı"
;;
tavuk | kanarya)
echo "$hayvan iki ayaklı"
;;
*)
echo "$hayvan için bilgi yok"
;;
esac
# çıktı: kedi dört ayaklıKılavuzdaki örnekten uyarlanan bu kod, case yapısının üç temel parçasını gösteriyor: | ile birleştirilen alternatif desenler, her dalı bitiren ;; ve en sonda her şeyi yakalayan * deseni. * boş metin dahil her şeyle eşleştiği için "hiçbiri tutmazsa" anlamında varsayılan dal olarak kullanılır. Kılavuz da bunu yaygın bir alışkanlık olarak anar.
Kısa dallar tek satıra da yazılabilir: start) baslat ;;. Komut listesi birden fazla komut içerebilir; komutları noktalı virgül ya da yeni satırla ayırırsınız. Dalın sonunu belirleyen şey ;; işaretidir, girinti değil.
Desenler glob kurallarına uyar
case eşleştirmesi, kılavuzun Pattern Matching bölümündeki kurallarla yapılır: * herhangi bir metinle (boş dahil), ? tek bir karakterle, [...] ise köşeli parantez içindeki karakterlerden biriyle eşleşir. [0-9] gibi aralıklar ve [[:space:]], [[:digit:]] gibi karakter sınıfları da kullanılabilir. Önemli bir fark: desen kelimenin tamamıyla eşleşmelidir; kelimenin bir parçasında eşleşme aramak için desenin başına ve sonuna * koymanız gerekir.
#!/usr/bin/env bash
for girdi in 5 42 x7 "" "a b"; do
case $girdi in
[0-9]) echo "[$girdi] tek basamak" ;;
[0-9][0-9]) echo "[$girdi] iki basamak" ;;
"") echo "[$girdi] boş" ;;
*[[:space:]]*) echo "[$girdi] boşluk içeriyor" ;;
*) echo "[$girdi] başka bir şey" ;;
esac
done
# çıktı:
# [5] tek basamak
# [42] iki basamak
# [x7] başka bir şey
# [] boş
# [a b] boşluk içeriyorBoş metni yakalamak için "") yazdık; iki tırnak işareti boş bir deseni temsil eder. Bir başka ayrıntı: case $girdi in satırında değişkeni tırnaklamadığımız halde "a b" değeri bölünmedi. Kılavuz, case kelimesinin tilde, parametre, komut, aritmetik ve süreç genişletmesinden ve tırnak kaldırmadan geçtiğini sayar; kelime bölme ve dosya adı genişletmesi bu listede yoktur. Yine de case "$girdi" in yazmak hiçbir şeyi bozmaz ve okuyan kişiye niyetinizi açıkça gösterir.
Desen dilinin tamamını, aralıkların yerel ayara göre nasıl değişebildiğini ve nullglob gibi seçenekleri Bash joker karakterler yazısında bulabilirsiniz. Düzenli ifade gerekiyorsa case yerine [[ $x =~ ... ]] kullanmalısınız; bunun için Bash regex eşleşmesi yazısına bakın.
Sıralama önemlidir: ilk eşleşen kazanır
case desenleri yukarıdan aşağıya dener ve ;; ile biten bir dal eşleştiğinde komut tamamen biter. Bu yüzden genel desenler, onları kapsayan özel desenlerden sonra gelmelidir. Dosya uzantılarını sınıflandırırken bu hata çok yapılır:
#!/usr/bin/env bash
dosya="yedek.tar.gz"
case $dosya in
*.gz) echo "gzip dosyası" ;;
*.tar.gz) echo "sıkıştırılmış tar arşivi" ;;
esac
# çıktı: gzip dosyası
case $dosya in
*.tar.gz) echo "sıkıştırılmış tar arşivi" ;;
*.gz) echo "gzip dosyası" ;;
esac
# çıktı: sıkıştırılmış tar arşiviİlk blokta *.tar.gz dalı hiçbir zaman çalışamaz, çünkü ona uyan her ad *.gz ile de eşleşir. Aynı nedenle *) varsayılan dalını her zaman en sona koyun; başa koyarsanız diğer dalların hiçbiri çalışmaz.
Tırnaklama ve değişkende desen tutmak

Desenler de genişletmeden geçer: kılavuza göre her desen tilde, parametre, komut, aritmetik ve süreç genişletmesine ve tırnak kaldırmaya uğrar. Bu, deseni bir değişkende tutabileceğiniz anlamına gelir. Tırnaklama ise burada da anlamı değiştirir: desenin tırnak içindeki kısmı harfi harfine eşleşir. * ya da ? karakterini gerçek bir karakter olarak aramak istiyorsanız onu tırnağa alır ya da ters bölü ile kaçırırsınız.
#!/usr/bin/env bash
desen="*.txt"
case "notlar.txt" in
$desen) echo "değişkendeki desenle eşleşti" ;;
esac
# çıktı: değişkendeki desenle eşleşti
case "notlar.txt" in
"$desen") echo "düz metin olarak eşleşti" ;;
*) echo "düz metin olarak eşleşmedi" ;;
esac
# çıktı: düz metin olarak eşleşmedi
case "?" in
\?) echo "gerçek soru işareti" ;;
esac
# çıktı: gerçek soru işaretiKullanıcıdan gelen bir değeri desen yerine koyuyorsanız onu mutlaka tırnağa alın; aksi halde kullanıcı * yazarak her dalı tetikleyebilir. Tırnakların genel kuralları için tek tırnak ve çift tırnak farkı yazısına bakabilirsiniz.
;;, ;& ve ;;& sonlandırıcıları
Bash, dal sonlandırıcı olarak ;; dışında iki seçenek daha sunar. Kılavuza göre ;; kullanıldığında ilk eşleşmeden sonra case biter. ;& kullanıldığında bir sonraki dalın komutları, deseni kontrol edilmeden çalıştırılır; C dilindeki switch yapısının "düşme" (fall through) davranışına benzer. ;;& kullanıldığında ise kabuk bir sonraki dalın desenini test eder ve eşleşirse onu da çalıştırır; yani her eşleşen dal çalışabilir.
#!/usr/bin/env bash
seviye="uyari"
case $seviye in
hata) echo "hata kaydı" ;&
uyari) echo "uyarı kaydı" ;&
bilgi) echo "bilgi kaydı" ;;
hata_ayiklama) echo "ayrıntılı kayıt" ;;
esac
# çıktı:
# uyarı kaydı
# bilgi kaydı
ad="rapor_2026.csv"
case $ad in
rapor_*) echo "rapor dosyası" ;;&
*2026*) echo "2026 yılına ait" ;;&
*.csv) echo "CSV biçiminde" ;;
*) echo "tanınmadı" ;;
esac
# çıktı:
# rapor dosyası
# 2026 yılına ait
# CSV biçimindeİlk örnekte uyari eşleşti; ;& nedeniyle hemen ardından bilgi dalının komutu da desen kontrolü yapılmadan çalıştı ve oradaki ;; yapıyı bitirdi. Bu, kayıt seviyesi gibi birbirini kapsayan kademeler için kullanışlıdır. İkinci örnekte her ;;& bir sonraki desenin denenmesini sağladı; *.csv dalı ;; ile bittiği için *) denenmedi. Kılavuzun Bourne kabuğuyla farkları listeleyen ekinde bu iki sonlandırıcı Bash'in eklediği özellikler arasında sayılır; sh ile de çalışması gereken betiklerde kullanmayın.
Büyük ve küçük harf duyarsız eşleşme
Varsayılan olarak case büyük ve küçük harfi ayırır. İki yaygın çözüm vardır. İlki, deseni [Ee]vet gibi karakter kümeleriyle yazmaktır. İkincisi, shopt -s nocasematch ile kabuk seçeneğini açmaktır; kılavuza göre bu seçenek açıkken case ve [[ eşleştirmeleri harf büyüklüğünü dikkate almaz. Seçeneği yalnızca ihtiyaç duyduğunuz yerde açıp sonra kapatmak, betiğin diğer bölümlerini etkilememek için iyi bir alışkanlıktır.
#!/usr/bin/env bash
for cevap in Evet e HAYIR; do
case $cevap in
[Ee]vet | [Ee]) echo "$cevap: onay" ;;
*) echo "$cevap: ret" ;;
esac
done
# çıktı:
# Evet: onay
# e: onay
# HAYIR: ret
shopt -s nocasematch
case "HAYIR" in
hayir) echo "nocasematch ile eşleşti" ;;
esac
shopt -u nocasematch
# çıktı: nocasematch ile eşleştiTürkçe metinlerde dikkatli olun: harf büyüklüğü dönüşümü yerel ayara bağlıdır ve C.UTF-8 gibi Türkçe olmayan bir yerel ayarda "I" ile "ı", "İ" ile "i" beklediğiniz gibi eşleşmeyebilir. Bu yüzden örnekte Türkçe özel harf içermeyen "hayir" yazımını kullandık. Harf dönüşümü için Bash string işlemleri yazısına da bakabilirsiniz.
Genişletilmiş desenler ve bir ayrıştırma tuzağı
shopt -s extglob açıkken case desenlerinde +(...), *(...), ?(...), @(...) ve !(...) gibi genişletilmiş desenler kullanılabilir. Kılavuz burada önemli bir uyarı yapar: extglob ayrıştırıcının davranışını değiştirdiği için, bu desenleri içeren yapılar ayrıştırılmadan önce seçeneğin açılmış olması gerekir. Bash bir satırı bütün olarak okuyup ayrıştırdığından, seçeneği aynı satırda açmak yetmez:
$ cat ayni-satir.sh
shopt -s extglob; case "abc123" in +([a-z])+([0-9])) echo "harf ve rakam";; esac
$ bash ayni-satir.sh
ayni-satir.sh: line 1: syntax error near unexpected token `('
ayni-satir.sh: line 1: `shopt -s extglob; case "abc123" in +([a-z])+([0-9])) echo "harf ve rakam";; esac'#!/usr/bin/env bash
shopt -s extglob
for kod in abc123 123 abc; do
case $kod in
+([a-z])+([0-9])) echo "$kod: harf ve rakam" ;;
+([0-9])) echo "$kod: yalnızca rakam" ;;
*) echo "$kod: başka" ;;
esac
done
# çıktı:
# abc123: harf ve rakam
# 123: yalnızca rakam
# abc: başkaSeçeneği betiğin başında, ayrı bir satırda açmak bu sorunu çözer. Fonksiyon gövdeleri de tanımlandıkları anda ayrıştırıldığı için, içinde genişletilmiş desen olan bir fonksiyonu tanımlamadan önce extglob açık olmalıdır. Desenlerin her birinin anlamı Bash extglob yazısında ayrıntılı olarak anlatılıyor.
case komutunun dönüş durumu
Kılavuza göre hiçbir desen eşleşmezse case 0 döndürür; bir dal çalıştıysa dönüş durumu son çalışan komut listesinin durumudur. Bu, case sonucunu doğrudan if ya da && ile kullanmayı zorlaştırır: "eşleşme olmadı" ile "eşleşti ve komut başarılı" aynı görünür. Eşleşmeme durumunu yakalamak istiyorsanız *) dalında kendiniz bir hata kodu döndürün.
#!/usr/bin/env bash
case "x" in
y) echo "y" ;;
esac
# $? = 0
case "x" in
x) false ;;
esac
# $? = 1if zinciri mi case mi?

Aynı kontrolü iki yapıyla da yazabilirsiniz, ama iki yapı farklı sorulara cevap verir. if herhangi bir komutun çıkış durumuna bakar; bu yüzden dosya testleri, sayısal karşılaştırmalar ve farklı değişkenleri bir arada sınamak için uygundur. case ise tek bir kelimeyi bir dizi desenle karşılaştırır. Değerin kendisine göre dallanıyorsanız, yani "bu metin şunlardan hangisi?" diye soruyorsanız case daha kısa ve okunaklıdır, çünkü değişkeni her dalda yeniden yazmanız gerekmez.
Örneğin bir dosya adının uzantısına bakan bir if zinciri, her dalda [[ $ad == *.jpg || $ad == *.png ]] gibi tekrarlar içerir. Aynı mantık case $ad in *.jpg | *.png) ... biçiminde tek satırda ifade edilir. Ayrıca case desenleri, [[ == ]] ile aynı eşleştirme kurallarını kullandığı için aradaki geçiş kolaydır: bir [[ ]] deseninde çalışan şey bir case dalında da çalışır. Bunun tersi olarak, iki koşulun birlikte doğru olmasını gerektiren durumlar ("dosya var ve boş değil") case ile doğal olarak ifade edilemez; orada if kullanın.
| Durum | Uygun yapı |
|---|---|
| Tek değişkeni birçok sabit değerle karşılaştırmak | case |
| Uzantı, önek gibi desenlere göre dallanmak | case |
| Dosya varlığı ve türü testleri | if ile [[ -f ]] ve benzerleri |
| Sayısal aralık kontrolü | if ile (( )) |
| Birden fazla değişkeni birlikte sınamak | if ile && ve || |
Küçük bir uygulama: alt komut dağıtıcı
case yapısının en yaygın kullanımı, bir betiğin ilk argümanına göre farklı işler yapmasıdır: betik.sh start, betik.sh stop gibi. Aşağıdaki örnek bunu bir fonksiyon içinde yapıyor; kısaltmaları | ile aynı dala bağlıyor, tanınmayan komutlar için kullanım mesajı yazıp 2 döndürüyor. Gerçek bir betikte fonksiyonu yonet "$@" biçiminde çağırırsınız. Çok sayıda seçenek ve bayrak ayrıştırmanız gerekiyorsa getopts ile seçenek ayrıştırma daha uygun bir araçtır.
#!/usr/bin/env bash
yonet() {
local komut=$1
case $komut in
start | baslat)
echo "servis başlatılıyor" ;;
stop | durdur)
echo "servis durduruluyor" ;;
restart)
yonet stop
yonet start ;;
"" | -h | --help)
echo "kullanım: yonet {start|stop|restart}" ;;
*)
echo "bilinmeyen komut: $komut"
return 2 ;;
esac
}
yonet baslat
yonet restart
yonet
yonet sil
# çıktı:
# servis başlatılıyor
# servis durduruluyor
# servis başlatılıyor
# kullanım: yonet {start|stop|restart}
# bilinmeyen komut: sil
# $? = 2restart dalının aynı fonksiyonu iki kez çağırması, kodu tekrar yazmadan dalları birleştirmenin basit bir yoludur. Gerçek servis yönetimi için bu tür bir betik yerine çoğu zaman systemd servis dosyası yazmak daha doğrudur; case ise küçük yardımcı betikler ve bakım araçları için idealdir.
Sık yapılan hatalar
- ;; unutmak: Bir dalın sonundaki
;;eksikse Bash bir sonraki dalın desenini komut sanır ve "syntax error near unexpected token `)'" hatası verir. Hata satırı, eksik;;işaretinden sonraki dalı gösterir. - Düzenli ifade yazmak:
casedesenleri glob desenidir.^abc$ya da[0-9]+gibi düzenli ifade yazımları beklediğiniz gibi çalışmaz;[0-9]+"bir rakam ve ardından gerçek bir artı işareti" anlamına gelir. - Varsayılan dalı başa koymak:
*)her şeyle eşleştiği için ondan sonraki dallar hiç çalışmaz. - Desen içinde boşluğu kaçırmamak: Boşluk içeren bir değeri eşleştirmek için deseni tırnağa alın ya da boşluğu kaçırın:
"iki kelime")veyaiki\ kelime). - Harf büyüklüğünü unutmak:
yes)deseni "YES" ile eşleşmez;[Yy][Ee][Ss]ya danocasematchkullanın.
Sık Sorulan Sorular
case içinde birden fazla değeri tek dalda nasıl yakalarım?
Desenleri | ile ayırın: start | baslat). Bu işaret burada bir boru hattı değildir; case sözdiziminde "veya" anlamındaki desen ayırıcıdır.
case ile sayı aralığı kontrol edebilir miyim?
Yalnızca karakter düzeyinde: [0-9] tek bir rakam, [1-5] 1 ile 5 arasındaki tek bir rakamla eşleşir. "18 ile 65 arası" gibi sayısal aralıklar için (( )) ve if kullanın; bu karşılaştırmalar Bash aritmetik işlemler yazısında anlatılıyor.
Desenin başındaki açılış parantezi ne işe yarar?
Kılavuz, desen listesinin başında isteğe bağlı bir ( kullanılabildiğini belirtir: (start) ...;;. Davranışı değiştirmez; bazı programcılar parantezlerin dengeli görünmesi için tercih eder. Bash 5.2.37 ile denediğimizde iki yazım da aynı sonucu verdi.
case dallarında atadığım değişkenler dışarıda geçerli mi?
Evet. case bir bileşik komuttur ve ( ) gibi bir alt kabuk oluşturmaz; dallar geçerli kabukta çalışır. Bu sayede case içinde bir ayar değişkenine değer verip yapı bittikten sonra kullanabilirsiniz. Alt kabukların değişkenleri nasıl kaybettirdiğini subshell ve çalışma ortamı yazısında görebilirsiniz.
#!/usr/bin/env bash
ortam="prod"
case $ortam in
prod) port=443 ;;
*) port=8080 ;;
esac
echo "port: $port"
# çıktı: port: 443case ile if arasında performans farkı var mı?
Kılavuzda bu konuda bir ölçüm ya da karşılaştırma yoktur. Seçimi okunabilirliğe göre yapın: tek bir değeri birçok sabitle ya da desenle karşılaştırıyorsanız case, farklı değişkenleri ya da sayısal koşulları sınıyorsanız if elif else daha doğal okunur.
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 · if elif else · joker karakterler · extglob · getopts · for döngüsü