tee Komutu ile Çıktıyı Hem Terminale Hem Dosyaya Yönlendirme
Sistem yöneticiliğinde en çok canımı sıkan şeylerden biri şu: bir komut çalıştırıyorsun, çıktı ekranda akıp gidiyor, bir sorun görüyorsun ama kaydedemedin. Ya da tam tersi, çıktıyı dosyaya yönlendirdin, terminalde göremiyorsun, ne olduğunu anlamak için dosyayı açman gerekiyor. İşte tam bu noktada tee komutu hayat kurtarıcı oluyor.
tee komutu, adını gerçek bir T-borusundan alıyor. Bir boru hattında akan veriyi ikiye ayırıyor: bir kısmı terminale gidiyor, diğer kısmı dosyaya yazılıyor. Basit ama kullanım alanları inanılmaz geniş. Yıllarca bu komutu yüzeysel kullandım, sonra bir gün production ortamında uzun süren bir deployment sırasında hem izlemem hem de loglamalı gereken bir durumla karşılaştım. O günden sonra tee benim için vazgeçilmez araçlardan biri haline geldi.
tee Komutunun Temel Çalışma Mantığı
tee komutu standart girdi (stdin) üzerinden aldığı veriyi aynı anda hem standart çıktıya (stdout) hem de belirtilen dosyaya yazar. Bunu bir musluk sistemi gibi düşünebilirsiniz: su hem lavaboya hem de biriktiriciye akıyor.
Temel sözdizimi şu şekilde:
komut | tee dosya_adi
En basit örnekten başlayalım:
ls -la /etc | tee etc_listesi.txt
Bu komutu çalıştırdığınızda /etc dizininin içeriği hem terminalinizde görünür hem de etc_listesi.txt dosyasına yazılır. Dosyayı sonradan açtığınızda terminalde gördüğünüz ile birebir aynı içeriği bulursunuz.
Şimdi temel parametrelere bakalım:
-a veya –append: Mevcut dosyanın üzerine yazmak yerine dosyanın sonuna ekler. Loglamada çok kritik bir parametre.
-i veya –ignore-interrupts: SIGINT sinyallerini görmezden gelir. Uzun süren işlemlerde Ctrl+C ile yanlışlıkla kesilmesini önler.
-p: Boru hatlarında hata durumunda sessizce devam eder, zorla devam etmek için kullanılır.
–output-error=MODE: Yazma hatalarında nasıl davranılacağını belirtir. warn, warn-nopipe, exit, exit-nopipe modları mevcuttur.
Gerçek Dünya Senaryosu: Sistem Kurulumunu Hem İzlemek Hem Kaydetmek
Diyelim ki bir sunucuya paket kurulumu yapıyorsunuz ve kurulum logunu kaydetmek istiyorsunuz ama süreç boyunca ne olduğunu da takip etmeniz gerekiyor:
apt-get install -y nginx postgresql redis-server | tee /var/log/kurulum_$(date +%Y%m%d_%H%M%S).txt
Burada date komutuyla dinamik bir dosya adı oluşturuyoruz. Her kurulum için ayrı bir log dosyası elde ediyorsunuz. Bu yaklaşım özellikle otomatik deployment scriptlerinde çok işe yarıyor çünkü hem operatör süreci izleyebiliyor hem de ileride referans için log kalıyor.
Peki ya birden fazla kurulum çalıştırıyorsanız ve tüm logları tek bir dosyada biriktirmek istiyorsanız? İşte -a parametresi devreye giriyor:
apt-get install -y nginx | tee -a /var/log/sistem_kurulum.log
apt-get install -y postgresql | tee -a /var/log/sistem_kurulum.log
apt-get install -y redis-server | tee -a /var/log/sistem_kurulum.log
Her komutun çıktısı aynı log dosyasına ekleniyor, önceki içerik kaybolmuyor.
Birden Fazla Dosyaya Aynı Anda Yazma
tee komutunun az bilinen özelliklerinden biri, aynı çıktıyı birden fazla dosyaya aynı anda yazabilmesidir:
komut | tee dosya1.txt dosya2.txt dosya3.txt
Pratikte bunu ne zaman kullanırsınız? Mesela hem genel bir log tutmak hem de belirli bir oturuma özel log oluşturmak istediğinizde:
./backup.sh | tee /var/log/backup/genel.log /var/log/backup/$(hostname)_$(date +%Y%m%d).log
Bu sayede hem sunucu adına göre organize edilmiş günlük bir log hem de tüm sunuculardan gelen genel log dosyanız oluyor. Merkezi log yönetiminde oldukça pratik.
tee ile Sudo Yetkisi Sorunu ve Çözümü
Sysadmin dünyasında sık karşılaşılan bir tuzak var. Şunu denediniz mi hiç?
echo "net.ipv4.ip_forward = 1" > /etc/sysctl.conf
Eğer root değilseniz bu komut “permission denied” hatası verir çünkü yönlendirme operatörü (>) shell tarafından işlenir ve shell’in yetkisi yeterli olmaz. sudo sadece echo komutuna uygulanır, yönlendirmeye değil.
Klasik çözüm şu şekildedir:
echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.conf
Ya da mevcut içeriğin üzerine yazmak yerine eklemek istiyorsanız:
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
Burada tee komutu sudo ile çalıştığı için root yetkisiyle dosyaya yazabiliyor. Bu tekniği /etc/hosts düzenlemesi, kernel parametresi değişikliği gibi sistem dosyalarına yazarken sıkça kullanıyorum. Shell scriptlerinde bu yaklaşım hem temiz hem de güvenli.
Bir adım daha gidelim. Terminalde görünmesini istemediğiniz ama sadece dosyaya kaydetmek istediğiniz durumlarda çıktıyı /dev/null‘a yönlendirebilirsiniz:
echo "127.0.0.1 myapp.local" | sudo tee -a /etc/hosts > /dev/null
Artık komut sessizce çalışır, terminale hiçbir şey yansımaz ama dosyaya yazma işlemi gerçekleşir. Otomasyon scriptlerinde gereksiz gürültüyü engellemek için harika bir yöntem.
Hata Çıktısını da Yakalamak: stderr ile Birlikte Kullanım
tee varsayılan olarak sadece stdout’u yakalar. Peki ya hata mesajlarını da kaydetmek istiyorsanız? Burada biraz bash yönlendirme bilgisi devreye giriyor.
komut 2>&1 | tee hata_ve_cikti.log
2>&1 ifadesi stderr’i stdout’a yönlendirir, ardından tee her ikisini birden yakalar. Sistem yönetiminde bunu çok kullanıyorum özellikle cron job çıktılarını loglarken:
#!/bin/bash
/usr/local/bin/backup.sh 2>&1 | tee -a /var/log/backup_cron.log
Ama burada dikkat edilmesi gereken bir nokta var: hem stdout hem stderr aynı akışa girince sıralama bazen karışabilir. Eğer stdout ve stderr’i ayrı dosyalarda tutmak ama aynı zamanda terminalde görmek istiyorsanız işler biraz daha karmaşık hale gelir:
komut > >(tee stdout.log) 2> >(tee stderr.log >&2)
Bu syntax ilk bakışta korkutucu görünüyor ama parçalara ayırdığınızda mantıklı: stdout process substitution ile tee‘ye gönderiliyor ve terminale yansıtılıyor, stderr ise ayrı bir tee örneğiyle işlenip terminal stderr’ine yönlendiriliyor.
Boru Hattı Ortasında Hata Ayıklama
tee komutunun en sevdiğim kullanım senaryolarından biri uzun boru hatlarında hata ayıklama. Karmaşık bir boru hattında bir şeyler yanlış gidiyorsa ve hangi aşamada sorun olduğunu bulmaya çalışıyorsanız, tee ile ara çıktıları kaydedebilirsiniz:
cat /var/log/nginx/access.log |
tee /tmp/adim1_ham_log.txt |
grep "404" |
tee /tmp/adim2_404_hatalar.txt |
awk '{print $1}' |
tee /tmp/adim3_ip_adresleri.txt |
sort | uniq -c | sort -rn |
tee /tmp/adim4_sayim.txt
Bu komut zinciri nginx access log’undan 404 hatası veren IP adreslerini sayıyor. Her aşamanın çıktısı ayrı bir geçici dosyaya kaydediliyor. Bir sorun olduğunda hangi aşamada sorun çıktığını /tmp/adim*.txt dosyalarına bakarak anlayabilirsiniz. Bu yaklaşım özellikle awk, sed, grep kombinasyonlarından oluşan karmaşık veri işleme pipeline’larında hayat kurtarıyor.
Gerçek Senaryo: Canlı Sistem Monitörü ile Log Tutma
Bir production sorunu sırasında sistemi izlerken aynı zamanda o anki durumu belgelemek gerekebilir. Şöyle bir durum düşünün: veritabanı sunucunuzda yük artışı var ve sorunun kaynağını araştırıyorsunuz:
#!/bin/bash
LOG_DOSYASI="/var/log/sistem_analiz_$(date +%Y%m%d_%H%M%S).log"
echo "=== Sistem Analizi Başlıyor: $(date) ===" | tee -a "$LOG_DOSYASI"
echo "--- Disk Kullanımı ---" | tee -a "$LOG_DOSYASI"
df -h | tee -a "$LOG_DOSYASI"
echo "--- Bellek Durumu ---" | tee -a "$LOG_DOSYASI"
free -h | tee -a "$LOG_DOSYASI"
echo "--- CPU Yükü ---" | tee -a "$LOG_DOSYASI"
uptime | tee -a "$LOG_DOSYASI"
echo "--- En Çok CPU Kullanan Procesler ---" | tee -a "$LOG_DOSYASI"
ps aux --sort=-%cpu | head -20 | tee -a "$LOG_DOSYASI"
echo "--- Aktif Ağ Bağlantıları ---" | tee -a "$LOG_DOSYASI"
ss -tuln | tee -a "$LOG_DOSYASI"
echo "=== Analiz Tamamlandı: $(date) ===" | tee -a "$LOG_DOSYASI"
echo ""
echo "Log dosyası: $LOG_DOSYASI"
Bu scripti çalıştırdığınızda her komutun çıktısını hem ekranda görürsünüz hem de zaman damgalı bir log dosyasına kaydedersiniz. Bir müşteriye ya da ekip arkadaşınıza “şu anda sistem şu durumdaydı” diye göndermek için hazır bir dökümantasyon elde etmiş olursunuz.
Ansible ve Deployment Süreçlerinde tee
DevOps tarafında çalışanlar için bir senaryo daha. Ansible playbook çalıştırırken çıktının hem terminalde görünmesini hem de loglanmasını sağlamak:
ansible-playbook -i inventory/production site.yml 2>&1 | tee -a /var/log/ansible/deploy_$(date +%Y%m%d_%H%M%S).log
Ama dikkat: Ansible’ın renkli çıktısı log dosyasına ANSI escape kodu olarak yazılır. Bunu temizlemek için sed ekleyebilirsiniz:
ansible-playbook -i inventory/production site.yml 2>&1 |
tee /tmp/ansible_raw.log |
sed 's/x1b[[0-9;]*m//g' |
tee /var/log/ansible/deploy_$(date +%Y%m%d_%H%M%S).log > /dev/null
Burada ilginç bir şey oluyor: /tmp/ansible_raw.log renkli çıktıyı alıyor (terminalde okuma için), /var/log/ansible/... ise temiz metni alıyor (arşiv ve grep için). Terminale çıktı yine de renkli gözüküyor çünkü tee‘nin ilk kopyası /tmp dosyasına yazılırken renkli veri hala stdout’ta akıyor.
watch Komutu ile Periyodik Loglama
tee ile watch veya döngü kombinasyonu da oldukça güçlü:
while true; do
echo "=== $(date +%H:%M:%S) ===" | tee -a /var/log/sistem_izleme.log
vmstat 1 1 | tee -a /var/log/sistem_izleme.log
sleep 30
done
Bu script her 30 saniyede bir vmstat çıktısını hem terminale hem log dosyasına yazıyor. Gece boyunca çalıştırıp sabah log dosyasını incelediğinizde sistemin gece nasıl davrandığını görebilirsiniz. Bazen en basit araçlar en güçlü sonuçları veriyor.
tee ile İnteraktif Oturum Kaydı
Sunucuda yaptığınız değişiklikleri belgelemek için şöyle bir yaklaşım kullanabilirsiniz. Bir script dosyasına tüm komutlarınızı yazıp tee ile çalıştırmak yerine, interaktif oturumu kaydetmek istediğinizde:
exec > >(tee -a /var/log/oturum_$(date +%Y%m%d_%H%M%S).log) 2>&1
Bu satırı bash script’inizin başına koyduğunuzda, script boyunca üretilen tüm çıktı hem terminale hem de log dosyasına gider. Uzun maintenance işlemlerinde baştan sona ne yaptığınızın kaydını tutmak için kullanışlı.
Sık Yapılan Hatalar ve Dikkat Edilmesi Gerekenler
Yıllar içinde tee kullanırken birkaç kez tökezledim, bunları paylaşmak istiyorum:
Dosya izinleri tuzağı: tee ile yazmaya çalıştığınız dizinde yazma izniniz yoksa komut hata verir ama bu hata boru hattının ortasında sessizce kaybolabilir. Her zaman hedef dizinin yazılabilir olduğunu kontrol edin.
-a parametresini unutmak: Bu benim en sık yaptığım hataydı. Birden fazla komutun çıktısını aynı dosyaya yazmak istediğinizde ilk komutta -a kullanmayı unutsanız sorun yok ama sonraki komutlarda mutlaka kullanmanız gerekiyor. Yoksa önceki komutun çıktısı silinir.
Büyük veri akışlarında performans: tee her veriyi hem stdout’a hem dosyaya yazdığı için, çok büyük veri akışlarında (örneğin büyük dosya kopyaları) bir miktar overhead oluşturabilir. Genel kullanımda bu hissedilmez ama terabaytlık veri işlediğinizde aklınızda olsun.
Tamponlama sorunları: Bazı programlar çıktıyı tamponladığı için tee ile birlikte kullanıldığında gerçek zamanlı görünmeyebilir. Bu durumda stdbuf -oL komut | tee dosya şeklinde tamponlamayı satır bazlı moda alabilirsiniz:
stdbuf -oL python3 uzun_script.py | tee -a /var/log/python_cikti.log
Sinyal yönetimi: Uzun süren bir işlemi Ctrl+C ile sonlandırdığınızda tee‘nin dosyaya yazdığı veri truncate olabilir. Kritik işlemlerde -i parametresini kullanmayı düşünebilirsiniz, ya da nohup ile birlikte çalıştırabilirsiniz.
tee Olmayan Alternatiflerin Neden Yetersiz Kaldığı
Bazıları şunu soruyor: neden tee kullanalım, script komutu veya sadece > yönlendirmesi yeterli değil mi?
script komutu tüm terminal oturumunu kaydeder, sadece belirli bir komutun çıktısını değil. Boru hattında kullanamaz, seçici değil.
Sadece > veya >> kullandığınızda terminalde hiçbir şey göremezsiniz. İzleme imkanınız yok.
>& ile kombinasyonlar işe yarar ama boru hattı ortasında kullanamaz, sadece komut sonu yönlendirmesinde geçerlidir.
tee ise boru hattının tam ortasına girebiliyor, seçici kullanılabiliyor, birden fazla hedefe yazabiliyor ve sudo ile yetkili dosya yazma sorununu çözüyor. Bu kombinasyon onu benzersiz yapıyor.
Sonuç
tee komutu, Linux dünyasında “küçük ama paha biçilmez” araçlar kategorisinin mükemmel bir örneği. Tek başına bakıldığında ne yapıyor diye sorabilirsiniz, ama boru hattı felsefesinin özünü temsil ediyor: küçük araçları bir araya getirerek güçlü çözümler oluşturmak.
Özellikle şu durumlar için tee‘yi kullanmayı alışkanlık haline getirmenizi öneririm: production ortamında yapılan her türlü değişiklik, uzun süren deployment ve kurulum süreçleri, sistem sağlığı kontrolleri ve periyodik izleme, karmaşık boru hatlarında hata ayıklama, ve sudo gerektiren dosyalara yazma işlemleri.
Bir sonraki sefere terminalde bir komut çalıştırırken “bunu kaydetmeli miydim?” diye düşündüğünüzde, cevabınız büyük ihtimalle “evet” olacak. Ve o an için tee tam orada hazır bekliyor olacak.
