Linux’ta Joker Karakterler: Glob ve Brace Expansion Kullanımı

Terminal ekranına bakıp “bu dosyaları tek tek mi silmem lazım?” diye düşündüğünüz oldu mu hiç? Ya da belirli bir uzantıya sahip yüzlerce dosyayı listelemek isteyip her birini manuel yazmak zorunda kaldığınız? İşte tam bu noktada joker karakterler devreye giriyor. Linux’un en güçlü özelliklerinden biri olan glob ve brace expansion, günlük sysadmin işlerini dakikalardan saniyelere indiriyor. Bu yazıda bu mekanizmaları derinlemesiyle inceleyeceğiz.

Joker Karakterler Nedir ve Shell Nasıl İşler Onları?

Önce temel bir kavramı netleştirelim: Joker karakterleri (wildcard) işleyen şey komutların kendisi değil, shell’in kendisidir. Yani ls .log yazdığınızda, ls komutu .log ifadesini görmez. Shell önce bu ifadeyi eşleşen dosya isimlerine genişletir, sonra ortaya çıkan listeyi ls‘ye argüman olarak verir.

Bu ayrım çok önemli çünkü bazen shell’in genişletme yapmasını istemeyebilirsiniz. O durumlarda tırnak işaretleri veya escape karakteri kullanmanız gerekir. Ama önce genişletmenin nasıl çalıştığını iyice anlayalım.

Shell’deki pattern matching iki ana kategoriye ayrılır:

  • Glob (Pathname Expansion): Dosya sistemi üzerinde çalışır, mevcut dosya ve dizin isimlerini eşleştirir
  • Brace Expansion: Dosya sisteminden bağımsız çalışır, listeler ve diziler oluşturur

Glob Karakterleri: Temel Jokerler

Yıldız (*) Karakteri

* karakteri, sıfır veya daha fazla karakteri temsil eder. En sık kullanılan joker karakterdir.

# Tüm .log uzantılı dosyaları listele
ls /var/log/*.log

# Belirli bir prefix ile başlayan tüm dosyaları bul
ls /etc/nginx/conf.d/site-*

# Herhangi bir alt dizindeki config dosyalarını listele (tek seviye)
ls /etc/*/config

# Tüm dosyaları sil (dikkatli kullanın!)
rm /tmp/cache-files/*

Burada dikkat etmeniz gereken bir nokta var: karakteri, nokta ile başlayan gizli dosyaları (dotfiles) eşleştirmez. ls yaptığınızda .bashrc veya .profile dosyaları listelenmez. Bunları dahil etmek için ls . veya ls {,.*} kullanmanız gerekir.

Soru İşareti (?) Karakteri

? karakteri tam olarak bir karakteri temsil eder. Dosya adının belirli bir pozisyonunda herhangi bir karakter olabileceğini ifade eder.

# Tam olarak 5 karakterli ismi olan .sh dosyaları
ls ?????.sh

# app1.log, app2.log, app3.log gibi dosyaları eşleştir
ls app?.log

# Belirli format log dosyalarını temizle
rm /var/log/app/error-2024-01-??.log

# access-01.log'dan access-99.log'a kadar
ls access-??.log

Gerçek dünya senaryosu olarak düşünün: Bir uygulama her gün report-DD.csv formatında dosya oluşturuyor ve siz sadece tek haneli günlere ait raporları işlemek istiyorsunuz. ls report-?.csv tam da bunu yapacaktır.

Köşeli Parantez ([]) Karakterleri

Belirli karakter kümelerini eşleştirmek için kullanılır. POSIX karakter sınıflarını da destekler.

# a, b veya c ile başlayan dosyalar
ls [abc]*.conf

# Rakamla başlayan dosyalar
ls /var/log/[0-9]*.log

# Büyük veya küçük harfle başlayan dosyalar
ls [a-zA-Z]*.sh

# log1, log2, log3 ama log4, log5 değil
ls log[123]

# Belirli sunucu log dosyaları (web01, web02, db01, db02)
ls /var/log/{web,db}0[12]*.log

# POSIX sınıfları ile harf ile başlayanlar
ls [[:alpha:]]*.txt

# Rakam içeren dosya isimlerini bul
ls *[[:digit:]]*

Köşeli parantez içinde ^ veya ! karakteri kullanırsanız negasyon oluşturur, yani o karakterler dışındaki her şeyi eşleştirir:

# Rakamla başlamayan dosyalar
ls [^0-9]*

# Sesli harf içermeyen dosyalar
ls *[^aeiouAEIOU]*

Brace Expansion: Dosya Sistemiyle Bağımsız Genişletme

Brace expansion glob’dan farklı olarak dosya sistemine bakmaz. Belirttiğiniz kombinasyonları oluşturur, bu kombinasyonlar mevcut dosyalara karşılık gelse de gelmese de. Bu özellik onu özellikle dosya ve dizin oluşturma işlemleri için çok kullanışlı kılar.

Temel Brace Expansion Sözdizimi

# Temel kullanım
echo {a,b,c}
# Çıktı: a b c

# String kombinasyonları
echo file{1,2,3}.txt
# Çıktı: file1.txt file2.txt file3.txt

# Birden fazla brace expansion birleştirme
echo {web,db,cache}-server-{01,02,03}
# Çıktı: web-server-01 web-server-02 web-server-03 db-server-01 db-server-02 db-server-03 cache-server-01 cache-server-02 cache-server-03

Bu özelliği kullanarak tek komutla karmaşık dizin yapıları oluşturabilirsiniz:

# Proje dizin yapısını tek komutla oluştur
mkdir -p myproject/{src/{controllers,models,views},tests/{unit,integration},docs,config}

# Birden fazla ortam için config dosyaları oluştur
touch config.{development,staging,production}.yml

# Yedekleme klasörleri oluştur
mkdir -p /backup/{daily,weekly,monthly}/{db,files,logs}

Sayısal Diziler (Sequence Expression)

Brace expansion ile sayısal veya alfabetik diziler oluşturabilirsiniz:

# 1'den 10'a kadar
echo {1..10}
# Çıktı: 1 2 3 4 5 6 7 8 9 10

# 0'dan 100'e 10'ar artarak
echo {0..100..10}
# Çıktı: 0 10 20 30 40 50 60 70 80 90 100

# Alfabetik dizi
echo {a..z}
# Çıktı: a b c d ... z

# Ters sıralama
echo {10..1}
# Çıktı: 10 9 8 7 6 5 4 3 2 1

# Sıfır doldurmlu sayılar (Bash 4+)
echo {01..10}
# Çıktı: 01 02 03 04 05 06 07 08 09 10

Bu özellik özellikle döngülerle kombinlendiğinde çok güçlü hale gelir:

# Birden fazla sunucu üzerinde işlem simülasyonu
for server in web-{01..05}; do
    echo "Connecting to $server..."
    # ssh $server "systemctl status nginx"
done

# Log rotasyonu için arşiv dosyaları oluştur
for month in {01..12}; do
    mkdir -p /archive/2024/${month}
done

Gerçek Dünya Senaryoları

Senaryo 1: Log Yönetimi ve Temizlik

Bir production sunucusunda disk dolmaya başladı ve eski log dosyalarını temizlemeniz gerekiyor. Şöyle bir durumla karşılaştığınızı düşünün: /var/log/app/ dizininde binlerce log dosyası var ve 2023’e ait olanları arşivleyip silmeniz lazım.

# 2023 yılına ait tüm log dosyalarını bul ve listele
ls /var/log/app/2023-{01..12}-*.log

# Bu dosyaları arşive taşı
mv /var/log/app/2023-{01..12}-*.log /archive/2023/

# Sadece Ocak-Mart arası error loglarını sil
rm /var/log/app/2023-0[123]-error*.log

# Tüm .tmp ve .bak uzantılı dosyaları temizle
find /var/log/app/ -name "*.{tmp,bak}" -delete
# Not: find ile brace expansion dikkatli kullanın, -name tek pattern alır
# Doğru kullanım:
find /var/log/app/ ( -name "*.tmp" -o -name "*.bak" ) -delete

# Birden fazla uzantıyı glob ile temizle
rm /var/log/app/*.{tmp,bak,old,gz.1,gz.2}

Senaryo 2: Toplu Dosya Yeniden Adlandırma

Geliştirme ortamında dosya adlandırma standartlarını değiştirdiniz ve eski dosyaları yeni formata çevirmeniz gerekiyor.

# .conf uzantılı dosyaları .cfg'ye yeniden adlandır (Bash rename komutu ile)
for f in /etc/myapp/*.conf; do
    mv "$f" "${f%.conf}.cfg"
done

# Veya daha kısa glob ile:
# web01.conf, web02.conf... gibi dosyaları yeniden adlandır
for i in {01..10}; do
    [ -f "web${i}.conf" ] && mv "web${i}.conf" "server-web${i}.cfg"
done

# Büyük harf uzantılı dosyaları küçük harfe çevir
for f in *.{JPG,PNG,TXT}; do
    [ -f "$f" ] && mv "$f" "${f,,}"
done

Senaryo 3: Çoklu Sunucu Konfigürasyon Dağıtımı

Nginx konfigürasyonunuzu birden fazla sunucuya dağıtmanız gerekiyor:

# Farklı ortamlar için config dosyaları oluştur
cp nginx.conf.template nginx-{development,staging,production}.conf

# Her ortam için ayrı SSL sertifika dizini oluştur
mkdir -p /etc/ssl/{development,staging,production}/{certs,private,csr}

# Birden fazla sanal host konfigürasyonu oluştur
for site in {site1,site2,site3,api,admin}; do
    cp /etc/nginx/template.conf /etc/nginx/sites-available/${site}.conf
    ln -s /etc/nginx/sites-available/${site}.conf /etc/nginx/sites-enabled/
done

# Belirli bir prefix ile başlayan tüm site configlerini devre dışı bırak
rm /etc/nginx/sites-enabled/test-*

Senaryo 4: Yedekleme Scriptleri

#!/bin/bash
# Günlük yedekleme scripti

BACKUP_DATE=$(date +%Y-%m-%d)
BACKUP_ROOT="/backup/${BACKUP_DATE}"

# Dizin yapısını oluştur
mkdir -p ${BACKUP_ROOT}/{databases,configs,logs}

# Tüm .sql dump dosyalarını yedekle
cp /tmp/*.sql ${BACKUP_ROOT}/databases/

# Önemli config dosyalarını yedekle
cp /etc/{nginx,apache2,mysql}/*.conf ${BACKUP_ROOT}/configs/ 2>/dev/null

# Son 7 günün loglarını sıkıştır
tar -czf ${BACKUP_ROOT}/logs/recent-logs.tar.gz /var/log/app/*-{1..7}.log 2>/dev/null

# 30 günden eski yedekleri temizle
find /backup/ -maxdepth 1 -type d -name "20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]" -mtime +30 -exec rm -rf {} ;

echo "Yedekleme tamamlandi: ${BACKUP_ROOT}"

Genişletilmiş Glob (extglob) Özellikleri

Bash’te shopt -s extglob komutuyla etkinleştirilen genişletilmiş glob özellikleri daha karmaşık pattern’lar oluşturmanızı sağlar:

# Genişletilmiş glob'u aktifleştir
shopt -s extglob

# ?(pattern) - Sıfır veya bir kez eşleşme
ls file?(s).txt    # file.txt veya files.txt

# *(pattern) - Sıfır veya daha fazla kez
ls *(backup-)*.tar.gz    # backup-file.tar.gz veya file.tar.gz

# +(pattern) - Bir veya daha fazla kez
ls +(error|warning)*.log    # error.log, warning.log, errorwarning.log, vb.

# @(pattern) - Tam olarak bir kez
ls @(error|warning).log    # Sadece error.log veya warning.log

# !(pattern) - Eşleşmeyen her şey
ls !(*.log)    # .log dışındaki tüm dosyalar
rm !(important-file.txt)    # important-file.txt dışındaki her şeyi sil (DİKKAT!)

!(pattern) özelliği özellikle güçlüdür. Bir dizinde çok sayıda dosya olduğunda belirli bir dosya dışındaki hepsini silmek veya işlemek için idealdir.

# Sadece config.yml'i koruyup geri kalanları temizle
shopt -s extglob
rm /tmp/!(config.yml)

# .log ve .txt dışındaki dosyaları arşivle
tar -czf archive.tar.gz !(*.log|*.txt)

Dikkat Edilmesi Gereken Durumlar

Boş Eşleşme Sorunu

Eğer glob pattern’ı hiçbir dosyayla eşleşmezse, bash varsayılan olarak pattern’ı olduğu gibi bırakır. Bu beklenmedik davranışlara yol açabilir:

# Eğer *.xyz uzantılı dosya yoksa, rm komutu "*.xyz" argümanını alır
rm *.xyz    # "rm: cannot remove '*.xyz': No such file or directory" hatası verir

# Bunu önlemek için nullglob kullanın
shopt -s nullglob
rm *.xyz    # Eşleşme yoksa hiçbir şey yapmaz

Boşluk İçeren Dosya İsimleri

Glob genişlemesinde boşluk içeren dosya isimleri sorun çıkarabilir:

# Yanlış kullanım - boşluklu isimler parçalanır
for f in *.txt; do
    process $f    # YANLIŞ
done

# Doğru kullanım
for f in *.txt; do
    process "$f"    # Tırnak içinde kullan
done

# Veya find ile daha güvenli yöntem
find . -name "*.txt" -exec process {} ;

Brace Expansion ve Değişkenler

Brace expansion, değişken genişletmesinden önce gerçekleşir. Bu nedenle brace expansion içinde değişken kullanamazsınız:

# Bu ÇALIŞMAZ
START=1
END=10
echo {$START..$END}    # {1..10} yerine {$START..$END} çıktısı verir

# Bunun yerine eval veya seq kullanın
eval echo {$START..$END}
seq $START $END

# Veya döngü değişkeni ile
for i in $(seq $START $END); do echo $i; done

Pratik İpuçları

Önce test edin, sonra uygulayın: Özellikle silme işlemleri öncesinde rm yerine echo veya ls kullanarak neyin eşleşeceğini kontrol edin:

# Önce listele
echo /var/log/*.log.gz
# Tamam görünüyorsa sil
rm /var/log/*.log.gz

Glob ve brace expansion birleştirme:

# Web sunucularının nginx access loglarını bul
ls /var/log/{web01,web02,web03}/nginx/access*.log

# Birden fazla uzantıyı kontrol et
ls /home/user/{documents,downloads}/*.{pdf,docx,xlsx}

ls yerine echo ile pattern test etme:

# Hangi dosyaların eşleşeceğini görmek için
echo *.{conf,cfg,ini}

# Veya printf ile her birini ayrı satırda
printf '%sn' *.log

Globstar ile özyinelemeli arama (shopt -s globstar ile):

shopt -s globstar

# Tüm alt dizinlerdeki .py dosyaları
ls **/*.py

# Tüm hiyerarşideki log dosyalarını sıkıştır
tar -czf all-logs.tar.gz **/*.log

Sonuç

Glob ve brace expansion, Linux’ta dosya sistemi üzerinde çalışırken size inanılmaz bir verimlilik kazandırır. *, ? ve [] karakterlerini doğru kullanmayı öğrendiğinizde, yüzlerce dosyayı tek bir komutla yönetebilirsiniz. Brace expansion ile de dosya sistemiyle bağımsız olarak karmaşık dizin yapıları ve dosya kombinasyonları oluşturabilirsiniz.

Önemli olan bu araçları önce anlayarak, sonra uygulayarak kullanmak. Özellikle rm komutuyla joker kullanırken önce echo veya ls ile test etmek kritik bir alışkanlık. Bir kez yanlış glob pattern ile önemli dosyaları sildiniz mi, bir daha test etmeyi ihmal etmiyorsunuz, inanın.

extglob özellikleri ise başlangıçta karmaşık görünse de karmaşık dosya filtreleme senaryolarında find komutuna iyi bir alternatif sunuyor. Özellikle !(pattern) formatı, “şu dosya hariç her şeyi işle” türünde operasyonlar için vazgeçilmez oluyor.

Bu pattern’ları scriptlerinizde ve günlük terminal kullanımınızda pratik yaparak özümsemenizi tavsiye ederim. Başlangıçta fazladan iki dakika test etmek, ilerleyen zamanlarda saatlerinizi kurtarır.

Yorum yapın