Bash tools
TEKNİK REHBER BASH TOOLS UNIX FELSEFESİ 2026

Unix felsefesi:
birleştirmek.

Küçük araçları birbirine bağlamak — bu Unix'i Unix yapan şey. Pipe, tee, xargs, process substitution. Her biri tek başına sınırlı, hepsi birlikte güçlü.

00 "Do one thing well"

Unix felsefesinin özü: Her program tek bir şeyi iyi yapsın ve diğerleriyle konuşabilsin.

Doug McIlroy'un 1978'de yazdığı kurallar bugün hâlâ geçerli:

UNIX FELSEFESİ

"Write programs that do one thing and do it well. Write programs to work together. Write programs to handle text streams, because that is a universal interface."

Çev.: "Tek bir şeyi iyi yapan programlar yaz. Birlikte çalışan programlar yaz. Metin akışlarını işleyen programlar yaz — çünkü bu evrensel bir arayüzdür."

Pipe'lar bu felsefenin beton dökümü. Tek başına sınırlı olan araçlar, pipeline'da birbirini tamamlar:

bash — pipeline örneği
# En sık erişilen 10 URL (nginx access.log'dan)
awk '{print $7}' /var/log/nginx/access.log \
  | sed 's/?.*//' \
  | sort \
  | uniq -c \
  | sort -rn \
  | head -10

Altı araç, her biri tek şey yapıyor. Birlikte bir rapor üretiyorlar.

01 stdin / stdout / stderr

Her sürecin üç standart akışı vardır. Birleştirmenin temeli burası.

  File descriptor:
    0 → stdin   (standart giriş)   — klavye veya pipe'tan
    1 → stdout  (standart çıkış)   — terminale veya pipe'a
    2 → stderr  (standart hata)    — hata mesajları

  Süreç:
    ┌─────────────────────┐
    │                     │
  stdin →  [ komut ]  → stdout
               │
             stderr
    

Yönlendirme operatörleri

bash
# stdout → dosya (üzerine yaz)
ls > liste.txt

# stdout → dosya (ekle)
echo "yeni satır" >> liste.txt

# stdin ← dosya
sort < veri.txt

# stderr → dosya
find / -name '*.conf' 2> hatalar.txt

# stdout ve stderr → aynı dosya
komut > cikti.txt 2>&1
# veya (bash kısaltması):
komut &> cikti.txt

# stderr → /dev/null (hataları bastır)
find / -name '*.log' 2>/dev/null

# stdout → /dev/null (çıktıyı bastır, sadece exit code iste)
grep -q 'pattern' dosya.txt > /dev/null
> dosya
stdout'u dosyaya yönlendir (üzerine yazar, varsa sıfırlar).
>> dosya
stdout'u dosyaya ekle.
< dosya
stdin'den oku.
2> dosya
stderr'i dosyaya yönlendir.
2>&1
stderr'i stdout'a yönlendir (ikisini birleştir). &> dosya kısaltması.
>/dev/null
çöpe at — çıktıyı yok say.
Sıra önemli: 2>&1

komut > dosya 2>&1 ile komut 2>&1 > dosya farklıdır. İlki her ikisini de dosyaya yönlendirir. İkincisi stderr'i önce stdout'a (terminale) bağlar, sonra stdout'u dosyaya yönlendirir — stderr terminale gelir. Her zaman > dosya 2>&1 sırası kullan.

02 Pipe (|) — akışı devret

Bir komutun stdout'unu bir sonraki komutun stdin'ine bağlar. İkisi eş zamanlı çalışır.

bash
# Temel pipe
ls -la | grep '^d'         # sadece dizinleri listele

# Zincirleme
cat /var/log/syslog | grep 'error' | tail -20

# Exit code: son komutun exit code'u pipeline'ın exit code'u
grep 'pattern' dosya.txt | wc -l
echo $?   # wc'nin exit code'u (genelde 0)

# pipefail: pipeline'da herhangi bir hata olunca başarısız say
set -o pipefail
grep 'yok' /dosya/yok.txt | wc -l
echo $?   # → 2 (grep'in exit code, dosya bulunamadı)

# PIPESTATUS: her komutun ayrı exit code'u
grep 'pattern' dosya.txt | sort | wc -l
echo ${PIPESTATUS[@]}   # → 0 0 0 (grep sort wc sırasıyla)
NE OLUYOR

Pipe'ta her iki komut eş zamanlı çalışır. sol_komut stdout'a yazar, sağ_komut aynı anda stdin'den okur. Aralarında kernel buffer var (4K-64K). Tam okuma/yazma döngüsü gerekmez — büyük akışlarda bellek dostu.

03 tee — akışı çatalla

tee (T-parçası) akışı ikiye böler: hem ileriye hem bir yere yazar.

bash
# Terminale yaz ve dosyaya kaydet
komut | tee cikti.txt

# Birden fazla dosyaya
komut | tee dosya1.txt dosya2.txt

# Ekle (-a append)
komut | tee -a log.txt

# Pipeline ortasında logla ve devam et
find . -name '*.log' \
  | tee /tmp/bulunan_loglar.txt \
  | xargs wc -l

# sudo ile dosyaya yaz (redirect'te sudo çalışmaz)
echo 'yeni satır' | sudo tee -a /etc/hosts

# Sadece dosyaya yaz (terminale değil)
# tee /dev/null yerine -a /dev/null ile hata önleme
komut | tee kayit.txt > /dev/null
sudo tee hilesi

sudo echo 'x' >> /etc/hosts çalışmaz — yönlendirme shell tarafından yapılır, sudo kapsamı dışında. Doğrusu: echo 'x' | sudo tee -a /etc/hosts — tee root olarak çalışır ve dosyaya yazar.

04 xargs — stdin'den argüman üret

Bir komutun çıktısını bir sonrakinin argümanı yap. Pipe'ın stdin'i desteklemeyen komutlarla çalışmasını sağlar.

bash
# Temel: her satırı argüman yap
echo "dosya1 dosya2 dosya3" | xargs rm
# → rm dosya1 dosya2 dosya3

# find ile klasik kombinasyon
find . -name '*.tmp' | xargs rm

# Kaçta kaç argüman: -n N (her çalıştırmada N argüman)
echo "a b c d" | xargs -n2 echo
# → echo a b
# → echo c d

# Argümanın yeri belirle (-I{})
ls *.jpg | xargs -I{} convert {} {}.webp
# {} her dosya adı için yer tutar

# NUL ayırıcı (-0, find -print0 ile güvenli)
find . -name '*.log' -print0 | xargs -0 gzip

# Paralel çalıştır (-P N: N süreç)
find . -name '*.jpg' -print0 | xargs -0 -P4 -I{} \
  convert {} -resize 800x600 out/{}

# Komut satırını göster (-t trace)
echo "a b c" | xargs -t echo

# Boş girişte çalışma (-r / --no-run-if-empty)
find . -name '*.tmp' | xargs -r rm
-n N
her çağrıda en fazla N argüman kullan.
-I yer_tutucu
argümanın komut içindeki yerini belirt. -I{} ile {} her argümanla değiştirilir.
-0 / --null
NUL ayırıcı kullan. find -print0 ile zorunlu çift.
-P N
N sürecini paralel çalıştır. CPU bağlı işlemler için hızlandırma.
-t / --verbose
çalıştırılan komutu stderr'e yaz.
-r / --no-run-if-empty
stdin boşsa komutu çalıştırma. rm gibi komutlarda hata önler.

05 Process substitution — <(cmd) ve >(cmd)

Bir komutun çıktısını dosya gibi kullan. Geçici dosyaya yazmadan iki komutun çıktısını karşılaştır.

bash
# Komutun çıktısını dosya gibi sun: <(cmd)
# diff iki dosya bekler; iki komutun çıktısını karşılaştır
diff <(ls /dir1) <(ls /dir2)

# sorted listeleri karşılaştır
diff <(sort a.txt) <(sort b.txt)

# comm: iki sıralı listenin kesişim/farkı
comm <(sort liste1.txt) <(sort liste2.txt)

# Uzak ve yerel dosyayı karşılaştır (geçici dosya gereksiz)
diff <(ssh prod cat /etc/nginx/nginx.conf) nginx.conf.local

# >(cmd): çıktı giden "dosya" olarak sun
tee >(gzip > log.gz) >(grep ERROR > errors.txt) < access.log
# access.log'u hem sıkıştır hem hataları ayrı çek
NE OLUYOR

<(ls /dir1) — bash bir FIFO veya /dev/fd/N oluşturur, ls /dir1 komutunun çıktısını oraya yazar. diff bu dosyayı okur. Geçici dosya oluşturmadan iki komutun çıktısı karşılaştırılmış olur. Bash ve zsh'ta çalışır, POSIX sh'da çalışmaz.

06 Command substitution — $(cmd)

Bir komutun çıktısını değişkene ata veya başka komutun argümanı yap.

bash
# Temel: $(cmd)
TARIH=$(date +%Y-%m-%d)
echo "Bugün: $TARIH"

# Komut argümanı olarak
ls $(cat dizin_listesi.txt)

# Dizine git (en fazla değişen dosyanın dizini)
cd $(git diff --name-only | head -1 | xargs dirname)

# İç içe
echo "Kernel: $(uname -r), python: $(python3 -V 2>&1 | awk '{print $2}')"

# Eski backtick sözdizimi (önerilmez)
TARIH=`date +%Y-%m-%d`   # iç içe yazılması zor, kaçış karakteri sorunlu
$(cmd)
modern, iç içe kullanım için temiz. Önerilen.
`cmd`
eski backtick stili. İç içe için ters eğik çizgi gerektirir (`outer \`inner\``). Kullanma.
Word splitting

Command substitution tırnak içinde kullanılmazsa sonuç kelime bölünmesine uğrar. file=$(ls "Benim Dosyam.txt") değil ls $file — dosya adındaki boşluk iki argümana bölünür. "$file" şeklinde tırnak içinde kullan.

07 Named pipe (FIFO)

Disk üzerinde kalıcı bir pipe noktası. İki farklı terminal veya süreç arasında akış sağlar.

bash
# FIFO oluştur
mkfifo /tmp/benim_pipe

# Terminal 1: yazar (bloke olur, okuyucu bağlanana kadar bekler)
ls -la > /tmp/benim_pipe

# Terminal 2: okuyucu
cat /tmp/benim_pipe

# Temizle
rm /tmp/benim_pipe

# Gerçek kullanım: iki program arasında veri akışı
mkfifo /tmp/video_pipe
# Terminal 1: veri üret (ffmpeg decode)
ffmpeg -i video.mp4 -f rawvideo -pix_fmt rgb24 /tmp/video_pipe

# Terminal 2: veri tüket (python ile işle)
python3 analiz.py < /tmp/video_pipe
FIFO ne zaman

Normal pipe tek bir shell içinde çalışır. FIFO (mkfifo) bağımsız iki süreç veya iki terminal arasında kalıcı bir "randevu noktası" sağlar. Biri yazar, diğeri okur — senkronizasyon kernel tarafından yapılır. Producer-consumer senaryoları için kullanışlıdır.

08 Pratik vakalar

Bölümlerdeki araçları gerçek senaryolarda birleştir.

Paralel curl — 10 URL'yi aynı anda indir

bash
# urls.txt: her satırda bir URL
cat urls.txt | xargs -P10 -I{} curl -sO {}
# -P10: 10 paralel curl, -s: sessiz, -O: URL'den dosya adı

İki log'u gerçek zamanlı karşılaştır

bash
diff <(ssh prod1 tail -f /var/log/app.log) \
     <(ssh prod2 tail -f /var/log/app.log)

Stream'i logla ama pipeline'da devam et

bash
kubectl logs -f mypod \
  | tee pod_$(date +%Y%m%d).log \
  | grep --line-buffered 'ERROR' \
  | while IFS= read -r line; do
      echo "$line" | mail -s "Pod Hatası" admin@example.com
    done

Büyük dosyayı paralel sıkıştır

bash
find /var/log -name '*.log' -mtime +7 -print0 \
  | xargs -0 -P4 zstd -T1 --rm

Değişken listeden API çağrısı

bash
# user_ids.txt: her satırda bir ID
cat user_ids.txt | xargs -I{} -P5 \
  curl -s "https://api.example.com/users/{}" \
  | jq -r '.name'

Log'daki en sık IP'yi bul ve engelle

bash
# En sık 429 alan IP → iptables'a ekle
awk '$9 == "429" {print $1}' access.log \
  | sort | uniq -c | sort -rn | head -5 \
  | awk '{print $2}' \
  | xargs -I{} sudo iptables -A INPUT -s {} -j DROP

Hatırlanacaklar

  • stdin=0, stdout=1, stderr=2. 2>/dev/null, 2>&1
  • Pipe: eş zamanlı çalışır, son komutun exit code'u pipeline'ın exit code'u
  • set -o pipefail: pipeline'da herhangi bir hata sonucu başarısız say
  • tee dosya: hem stdout'a hem dosyaya, tee -a ekle, sudo tee root dosyaları için
  • xargs -I{}: argümanın yeri, -P N: paralel, -0: NUL ayırıcı
  • <(cmd): komut çıktısını dosya gibi sun (diff, comm için)
  • $(cmd): komut çıktısını değişkene veya argümana çevir
  • mkfifo: iki bağımsız süreç arasında kalıcı pipe noktası
  • find -print0 | xargs -0: boşluklu dosya adı güvenliği

Pipeline'larda metin işleme için: grep, sed, awk rehberi.

find ile xargs kombinasyonları için: find rehberi.