tee Komutu ile Çıktıyı Hem Ekrana Yazdırma Hem Dosyaya Kaydetme
Bir sistem yöneticisinin en çok nefret ettiği şeylerden biri şudur: uzun süren bir işlemin çıktısını izlerken “acaba kaydettim mi?” diye paniklemek. Script çalıştı, ekranda bir sürü şey aktı, ama siz o anda başka bir şeyle ilgileniyordunuz ve kritik bir hata mesajı gözünüzden kaçtı. İşte tam bu noktada tee komutu hayat kurtarıcı oluyor.
tee komutunu bir T-kavşağı gibi düşünebilirsiniz; standart girişten aldığı veriyi hem standart çıkışa (yani ekrana) hem de belirttiğiniz dosyaya eş zamanlı olarak yazıyor. Adını da zaten bu T şeklinden alıyor. Basit görünüyor, değil mi? Ama bu basit konsept üzerine kurulabilecek kullanım senaryoları inanılmaz derecede zengin.
tee Komutunun Temel Mantığı
Linux’ta komutları pipe (|) ile birleştirdiğinizde, bir komutun çıktısı doğrudan bir sonrakine giriyor. Bu harika bir şey, ama ara çıktıları kaybediyorsunuz. tee burada devreye girerek pipe zincirini kırmadan çıktıyı “ele geçiriyor” ve bir dosyaya yazıyor.
Basit sözdizimi şu şekilde:
komut | tee dosya.txt
Ve hemen pratik bir örnek:
ls -la /etc | tee /tmp/etc_listesi.txt
Bu komutu çalıştırdığınızda /etc dizininin içeriği hem terminalinizde görünecek hem de /tmp/etc_listesi.txt dosyasına kaydedilecek. Dosyayı sonradan incelemek, başka birine göndermek veya bir script’te kullanmak için elinizin altında olacak.
Temel Parametreler
-a (–append): Varsayılan davranışta tee hedef dosyayı her seferinde sıfırdan oluşturur. -a parametresiyle dosyanın sonuna ekleme yapabilirsiniz.
-i (–ignore-interrupts): SIGINT sinyalini (Ctrl+C) yok sayar. Uzun süren işlemlerde interrupt sinyalinin dosya yazımını yarıda kesmesini engellemek için kullanılır.
–output-error: Dosyaya yazma hatası oluştuğunda davranışı kontrol eder. warn, warn-nopipe, exit, exit-nopipe gibi değerler alabilir.
Şimdi bu parametreleri gerçek senaryolarda görelim.
Gerçek Dünya Senaryoları
Senaryo 1: Sistem Güncellemesi Sırasında Log Tutmak
Üretim sunucusunda paket güncellemesi yapıyorsunuz. Her şeyin kaydının olmasını istiyorsunuz çünkü bir şeyler ters giderse ne yüklendiğini, hangi bağımlılıkların değiştiğini bilmeniz gerekiyor.
sudo apt-get upgrade -y | tee /var/log/upgrade_$(date +%Y%m%d_%H%M%S).txt
date +%Y%m%d_%H%M%S ifadesi sayesinde her çalıştırmada benzersiz bir dosya adı oluşturuluyor. Bir hafta sonra “pazar günü ne yüklendi?” diye sorduğunuzda elinizde net bir kayıt olacak.
Senaryo 2: Append Modunda Sürekli Log Tutmak
Bir servisin durumunu belirli aralıklarla kontrol edip aynı dosyaya ekliyorsunuz:
while true; do
systemctl status nginx | head -20 | tee -a /var/log/nginx_health.log
echo "--- $(date) ---" | tee -a /var/log/nginx_health.log
sleep 300
done
-a olmadan her 5 dakikada bir dosyanın üzerine yazılırdı ve önceki kayıtlar giderdi. Bu şekilde tüm geçmişi saklıyorsunuz.
Senaryo 3: Birden Fazla Dosyaya Aynı Anda Yazmak
tee aynı anda birden fazla dosyaya yazabilir. Bunu pek çok kişi bilmiyor:
df -h | tee /tmp/disk_raporu.txt /var/log/disk_raporu.log ~/Desktop/disk_raporu.txt
Aynı çıktıyı üç farklı konuma kaydettin, ekranda da gördün. Mesela bir raporu hem log dizinine hem de bir ekip üyesinin erişebileceği paylaşımlı alana aynı anda yazmanız gerektiğinde çok işe yarıyor.
Senaryo 4: Sudo ile Dosyaya Yazmak
Bu klasik bir tuzak. Şunu deneyin:
sudo echo "net.ipv4.ip_forward=1" > /etc/sysctl.conf
Hata alırsınız çünkü yönlendirme (>) sudo’nun kapsamı dışında çalışır, normal kullanıcı izniyle dosyaya yazmaya çalışır. tee burada şöyle kullanılır:
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.conf
Ya da mevcut içeriği koruyarak eklemek isterseniz:
echo "vm.swappiness=10" | sudo tee -a /etc/sysctl.conf
Bu pattern’i öğrendikten sonra hayatınız kolaylaşıyor. Özellikle /etc altındaki konfigürasyon dosyalarını komut satırından düzenlerken çok sık kullanıyorsunuz.
Senaryo 5: Pipe Zinciri İçinde Ara Çıktı Kaydetmek
teenin gerçek gücü, uzun pipe zincirlerinin ortasında çalışabilmesidir. Çıktı teeden geçtikten sonra bir sonraki komuta da gidiyor:
cat /var/log/auth.log | grep "Failed password" | tee /tmp/basarisiz_girisler.txt | awk '{print $11}' | sort | uniq -c | sort -rn | tee /tmp/ip_sayim.txt
Burada ne oluyor:
auth.log‘dan başarısız giriş denemelerini çekiyorsunuz- Ham satırları
/tmp/basarisiz_girisler.txt‘e kaydediyorsunuz - Sonra IP adreslerini ayıklayıp sayım yapıyorsunuz
- Sayım sonuçlarını da
/tmp/ip_sayim.txt‘e kaydediyorsunuz - Her iki ara çıktıyı da ekranda görebiliyorsunuz
Bunu tee olmadan yapmak için komutu iki kez çalıştırmanız ya da geçici dosyalar kullanmanız gerekirdi.
Senaryo 6: Script Çıktısını Kaydetmek
Uzun süren bir bakım scripti yazıyorsunuz ve hem operatörün ekranda görmesini hem de loglanmasını istiyorsunuz. Script içinde her komut için ayrı ayrı tee kullanmak yerine script’in kendi çıktısını yönlendirip teeye bağlayabilirsiniz:
#!/bin/bash
exec > >(tee -a /var/log/bakim_$(date +%Y%m%d).log) 2>&1
echo "Bakım başlıyor: $(date)"
echo "Disk durumu kontrol ediliyor..."
df -h
echo "Bellek durumu:"
free -h
echo "Servis durumları:"
systemctl is-active nginx mysql redis
echo "Bakım tamamlandı: $(date)"
exec > >(tee -a ...) satırı script’in geri kalanındaki tüm stdout çıktısını hem ekrana hem dosyaya yönlendiriyor. 2>&1 ise hata mesajlarını da bu akıma dahil ediyor. Bu pattern’i production script’lerinizde mutlaka kullanmanızı öneririm.
Senaryo 7: Hata Çıktısını Ayırarak Kaydetmek
Bazen stdout ve stderr’i ayrı dosyalara kaydetmek istersiniz ama ekranda her ikisini de görmek istersiniz:
./uzun_sureli_islem.sh
> >(tee /tmp/islem_stdout.log)
2> >(tee /tmp/islem_stderr.log >&2)
Bu biraz karmaşık görünüyor, açıklayalım:
> >(tee /tmp/islem_stdout.log): stdout’u tee’ye gönder, tee hem dosyaya yazar hem stdout’a çıkar2> >(tee /tmp/islem_stderr.log >&2): stderr’i tee’ye gönder, tee hem dosyaya yazar hem stderr’e çıkar (>&2 ile)
Bu sayede terminalinizde her şeyi görüyorsunuz ama hatalar ayrı bir log dosyasında. Sabah gelip “gece ne olmuş” diye baktığınızda hata dosyasına bakmanız yeterli.
tee ile Birlikte Sık Kullanılan Kombinasyonlar
grep ile Filtreleyerek Kaydetmek
journalctl -f | tee /tmp/tum_loglar.txt | grep -i "error|warn|critical" | tee /tmp/hata_loglar.txt
Tüm logları bir dosyaya, sadece hata ve uyarıları başka bir dosyaya kaydediyorsunuz. Gerçek zamanlı izleme yapıyorsunuz.
curl ile İndirirken İçeriği İşlemek
curl -s https://api.example.com/servers | tee /tmp/api_yaniti.json | python3 -m json.tool
API yanıtını ham halde kaydedip aynı zamanda düzenli görüntülüyorsunuz. Hata ayıklamada çok işe yarıyor; ham yanıt dosyada var, siz de terminalde okunabilir görüyorsunuz.
make veya Derleme Süreçlerinde
make -j4 2>&1 | tee build_log.txt
Derleme süreci hem ekranda akıyor hem kayıt altında. Derleme hatası oluştuğunda grep -n "error" build_log.txt ile hataları hızlıca bulabiliyorsunuz.
Dikkat Edilmesi Gereken Noktalar
Tamponlama (Buffering) Sorunu: Bazı durumlarda teeye gelen veri satır satır değil, bloklar halinde işleniyor. Bu gerçek zamanlı izlemede gecikmeye yol açabilir. stdbuf komutu ile bu davranışı değiştirebilirsiniz:
stdbuf -oL uzun_sureli_komut | tee cikti.txt
-oL parametresi satır bazlı tamponlama yapılmasını sağlar.
Dosya İzinleri: tee yazma iznini olan bir dosyaya yazabilir. Yoksa hata verir ama çıktıyı ekranda göstermeye devam eder. Bu bazen istenmeyen bir davranış olabilir; kritik logları kaybetmeden önce dosya izinlerini kontrol edin.
Disk Alanı: Özellikle -a modunda ve yoğun çıktı üreten komutlarda disk dolabilir. logrotate ile entegre etmek ya da tee ile birlikte boyut kontrolü yapmak iyi bir pratik:
# Dosya 100MB'ı geçince yeni dosya oluştur
komut | tee >(split -b 100m - /var/log/buyuk_cikti_)
Sinyal Yönetimi: Uzun süren işlemleri Ctrl+C ile keserseniz tee de durur. -i parametresi ile bu davranışı değiştirilebilir ama genellikle doğal davranış beklenen şeydir.
Pratik Bir Use Case: Sunucu Sağlık Kontrol Scripti
Gerçekten kullandığım bir örneği paylaşayım. Yeni bir sunucuyu teslim alırken ya da sorun şüphesi olduğunda çalıştırdığım temel kontrol scripti:
#!/bin/bash
LOG_FILE="/tmp/sunucu_kontrol_$(hostname)_$(date +%Y%m%d_%H%M%S).txt"
kontrol() {
echo ""
echo "========================================" | tee -a "$LOG_FILE"
echo "$1" | tee -a "$LOG_FILE"
echo "========================================" | tee -a "$LOG_FILE"
shift
"$@" 2>&1 | tee -a "$LOG_FILE"
}
echo "Kontrol başlangıcı: $(date)" | tee "$LOG_FILE"
kontrol "CPU Bilgisi" lscpu | grep -E "Model name|CPU(s)|Thread"
kontrol "Bellek Kullanımı" free -h
kontrol "Disk Kullanımı" df -hT
kontrol "Son 1 Saatteki Yük" uptime
kontrol "Ağ Bağlantıları" ss -tuln
kontrol "Çalışan Servisler" systemctl list-units --state=running --type=service
kontrol "Son 50 Kernel Mesajı" dmesg | tail -50
kontrol "En Çok CPU Kullanan 10 Proses" ps aux --sort=-%cpu | head -11
echo ""
echo "Kontrol tamamlandı: $(date)" | tee -a "$LOG_FILE"
echo "Rapor kaydedildi: $LOG_FILE" | tee -a "$LOG_FILE"
Bu script çalışırken her şeyi ekranda görüyorsunuz ve aynı zamanda timestamp’li bir dosyaya kaydediliyor. Müşteriyle ya da ekiple paylaşmak istediğinizde elinizde hazır bir rapor var.
tee mi, Yönlendirme mi?
Sık sorulan bir soru: “Neden komut > dosya.txt yazmıyorum da tee kullanıyorum?” Cevap basit: yönlendirme kullandığınızda çıktı sadece dosyaya gidiyor, ekranda göremiyorsunuz. tee size ikisini birden veriyor.
Ama bunun ötesinde, tee pipe zincirinin ortasında kullanılabilir. Yönlendirme bunu yapamaz. komut1 | komut2 > dosya | komut3 gibi bir şey çalışmaz çünkü yönlendirme pipe’ı kırar. tee ise pipe’ın içinde çalışarak veriyi hem dosyaya hem bir sonraki komuta iletiyor.
Bir diğer fark: sudo senaryosu. Yukarıda bahsettiğim echo "..." | sudo tee /dosya pattern’i buna güzel bir örnek. Salt yönlendirme ile bu problemi zarif şekilde çözemezsiniz.
Sonuç
tee komutu, görünürdeki basitliğinin arkasında ciddi bir iş gücü barındırıyor. Temel kullanımı iki dakikada öğreniliyor ama derinlerine indikçe, özellikle script yazarken ve karmaşık pipe zincirleri kurarken ne kadar vazgeçilmez olduğu ortaya çıkıyor.
Benim için en kritik kullanım senaryoları şunlar oldu: sudo ile yetkili dosyalara yazma, üretim sunucularında gerçekleştirilen bakım işlemlerinin loglanması ve uzun derleme süreçlerinin takibi. Her üçünde de tee olmadan ya işi yarım bırakmak zorunda kalıyordunuz ya da çok daha karmaşık çözümlere gitmek zorunda kalıyordunuz.
Eğer terminal komutlarına yeni başlıyorsanız, teeyi öğrenmek için en iyi zaman şimdi. Eski bir sysadmin alışkanlığıyla söylüyorum: loglanmayan her işlem, ileride açıklaması olmayan bir sorunun tohumudur. tee bu tohumların büyümesini erkenden engeller.
