Linux'ta terminale yeni alışan pek çok kişi komutları tek tek, birbirinden kopuk çalıştırır: bir komutun çıktısını gözüyle okur, ilgili satırı fareyle seçip kopyalar, sonra bir başka komuta yapıştırır. Oysa Unix felsefesinin belkemiği tam da bunun tersidir. "Her program tek bir işi iyi yapsın, çıktısı bir başka programın girdisi olabilsin" ilkesi, Linux'u bu kadar güçlü kılan şeyin ta kendisidir. Bir komutun çıktısını başka bir komuta akıtan | (pipe) işareti ve çıktıyı dosyaya yazan > operatörü, bu felsefeyi günlük işinize taşıyan iki küçük ama devrimci araçtır.
Bu rehberde standart giriş, standart çıkış ve standart hata akışlarının ne olduğunu; çıktıyı dosyaya nasıl yönlendireceğinizi; hata mesajlarını normal çıktıdan nasıl ayıracağınızı; komutları pipe ile nasıl zincirleyeceğinizi ve tee ile bir çıktıyı hem ekranda görüp hem dosyaya kaydetmeyi kıdemli bir sistem yöneticisinin bakış açısıyla, gerçek log ve komut örnekleriyle işleyeceğiz. Temel terminal komutlarına henüz aşina değilseniz önce temel Linux komutları yazımıza göz atmanızı, ardından buraya dönmenizi öneririm; çünkü buradaki her örnek ls, grep, cat gibi komutların üzerine kuruludur.
Üç Temel Akış: stdin, stdout ve stderr#
Linux'ta her çalışan program açılışta üç adet standart veri kanalıyla dünyaya bağlanır. Bunlar sayısal tanımlayıcılarıyla (file descriptor) anılır ve tüm yönlendirme mantığı bu üçlünün üzerine oturur.
| Akış | Numara | Görevi | Varsayılan hedef |
|---|---|---|---|
| stdin | 0 | Programın girdi aldığı kanal | Klavye |
| stdout | 1 | Normal çıktının yazıldığı kanal | Ekran (terminal) |
| stderr | 2 | Hata ve uyarı mesajlarının yazıldığı kanal | Ekran (terminal) |
Kritik nokta şudur: stdout ve stderr farklı kanallardır ama varsayılan olarak ikisi de aynı ekrana düşer. Bu yüzden terminalde hepsi karışık görünür ve çoğu kişi ikisinin ayrı olduğunu fark etmez. Örneğin aşağıdaki komutta /etc klasörü var ama /yokdizin yok:
ls /etc /yokdizin
Ekranda hem /etc'nin dosya listesini (stdout) hem de ls: /yokdizin: Böyle bir dosya ya da dizin yok hatasını (stderr) yan yana görürsünüz. Yönlendirmenin bütün gücü, bu iki kanalı ayrı ayrı yakalayıp istediğiniz yere göndermekten gelir. Bir program neden girdi beklerken donup kalır? Çünkü stdin'i klavyeye bağlıdır ve siz bir şey yazana kadar bekler. cat komutunu argümansız çalıştırıp Ctrl+D ile bitirmeyi denerseniz bu davranışı canlı görebilirsiniz.
Çıktıyı Dosyaya Yönlendirmek: > ve >>#
En sık kullanacağınız operatör, stdout'u ekran yerine bir dosyaya yazan > işaretidir. Bu operatör hedef dosyayı sıfırlayarak yazar; dosya varsa içeriğini tamamen siler ve yerine yeni çıktıyı koyar.
# date komutunun çıktısını dosyaya yaz (dosya varsa üzerine yaz)
date > zaman.txt
# ps çıktısını rapor dosyasına aktar
ps aux > surecler.txt
İçeriği silmeden sona eklemek istiyorsanız çift ok >> kullanılır. Bu, log biriktirmenin ve bir dosyaya zaman içinde satır satır not düşmenin standart yoludur:
# Her çalıştırdığında dosyanın sonuna yeni satır ekler
echo "Yedek başladı: $(date)" >> /var/log/yedek.log
echo "Yedek bitti: $(date)" >> /var/log/yedek.log
Aradaki fark tek karakter olsa da sonuçları çok farklıdır. > ile yanlış dosyayı hedeflerseniz o dosyanın eski içeriği geri dönüşü olmadan gider. Bu yüzden önemli bir dosyaya yazarken iki kez düşünün. Küçük bir güvenlik ağı olarak, kabuğa set -o noclobber ayarını verebilirsiniz; bu ayar açıkken > var olan bir dosyanın üzerine yazmayı reddeder, gerçekten üzerine yazmak istediğinizde >| yazmanız gerekir.
Aşağıdaki tablo yönlendirme operatörlerinin tamamını özetliyor:
| Operatör | Ne yapar |
|---|---|
> | stdout'u dosyaya yazar (üzerine yazar) |
>> | stdout'u dosyanın sonuna ekler |
2> | stderr'i dosyaya yazar (üzerine yazar) |
2>> | stderr'i dosyanın sonuna ekler |
&> veya >& | stdout ve stderr'i birlikte dosyaya yazar |
< | Dosyayı komutun stdin'i olarak verir |
2>&1 | stderr'i stdout'un gittiği yere yönlendirir |
Hata Akışını Ayırmak: 2> ve /dev/null#
Şimdi asıl inceliğe geliyoruz. > yalnızca stdout'u (kanal 1) yakalar; hata mesajları stderr (kanal 2) üzerinden geldiği için yine ekranda kalır. Bunu bizzat görmek için:
ls /etc /yokdizin > cikti.txt
Bu komuttan sonra cikti.txt içinde yalnızca /etc'nin listesi olur; hata satırı ise hâlâ ekrana düşer, çünkü onu yakalamadık. Hatayı ayrı bir dosyaya almak için kanal numarasını operatörün önüne yazarız:
# Normal çıktı bir dosyaya, hatalar başka bir dosyaya
ls /etc /yokdizin > cikti.txt 2> hatalar.txt
Çoğu zaman hata mesajlarıyla hiç ilgilenmez, onların ekranı kirletmesini istemezsiniz. İşte burada Linux'un "kara delik" dosyası olan /dev/null devreye girer; oraya yazılan her şey sessizce yok olur:
# find çalışırken erişilemeyen dizin uyarılarını tamamen sustur
find / -name "*.conf" 2> /dev/null
# Bir servisi yeniden başlat, hem çıktı hem hatayı yok say
systemctl restart nginx &> /dev/null
İki akışı tek bir dosyada birleştirmek için iki yol vardır. Modern kabuklarda kısa yol &>'dir:
# stdout + stderr birlikte tek dosyaya
./yedek.sh &> yedek-tam.log
Klasik ve taşınabilir yol ise 2>&1 yazımıdır; "kanal 2'yi, kanal 1'in şu an gittiği yere yolla" demektir. Burada sıralama hayatidir: yönlendirmeler soldan sağa değerlendirilir, bu yüzden 2>&1 mutlaka dosya yönlendirmesinden sonra gelmelidir.
# DOĞRU: önce stdout dosyaya gider, sonra stderr de aynı dosyaya
komut > tumu.log 2>&1
# YANLIŞ: 2>&1 önce gelirse stderr hâlâ ekrana giden stdout'a bağlanır,
# sonra sadece stdout dosyaya döner; hatalar yine ekranda kalır
komut 2>&1 > tumu.log
Pipe ile Komutları Zincirlemek: |#
Yönlendirme çıktıyı bir dosyaya gönderirken, pipe (|) onu doğrudan başka bir komutun stdin'ine akıtır. Aradaki dosya adımını ortadan kaldırır ve komutları küçük yapı taşları gibi birbirine bağlamanızı sağlar. Zincirdeki her komut, kendinden öncekinin çıktısını girdi olarak alır.
Basit bir örnekle başlayalım. Kaç kullanıcı hesabı olduğunu öğrenmek için /etc/passwd dosyasının satırlarını wc -l ile sayabiliriz:
cat /etc/passwd | wc -l
Gücü asıl birkaç halkayı arka arkaya dizince görürsünüz. Sunucuda en fazla bellek tüketen beş süreci bulalım:
ps aux | sort -rnk 4 | head -n 5
Burada üç komut sırayla çalışır: ps aux tüm süreçleri listeler, sort -rnk 4 çıktıyı dördüncü sütuna (bellek yüzdesi) göre tersten sıralar, head -n 5 de yalnızca ilk beş satırı gösterir. Her komut kendi işini yapar, birleşince güçlü bir teşhis aracına dönüşürler.
Bir başka klasik: bir kelimenin geçtiği süreçleri bulmak. Ama grep deseninin kendisinin sonuçta görünmemesi için küçük bir hile kullanılır:
ps aux | grep '[n]ginx'
Köşeli parantezli [n]ginx deseni "nginx" ile eşleşir ama grep [n]ginx komutunun kendisi metin olarak [n]ginx içerdiği için kendi kendini yakalamaz. Böyle küçük pratikler, pipe ile çalışmayı gerçekten verimli kılan detaylardır.
Zincirin her halkasının önceki halkaya bağımlı olduğunu unutmayın; ortadaki bir komut hata verirse sonraki komut boş girdi alır. Zincirin nerede bozulduğunu görmek için komutları teker teker ekleyerek ilerlemek en sağlıklı yöntemdir.
tee ile Hem Ekrana Hem Dosyaya Yazmak#
Pipe ile ilgili can sıkıcı bir durum vardır: bir komutun çıktısını bir dosyaya kaydetmek istersiniz ama aynı anda ekranda da görmek, hatta zincire devam etmek istersiniz. > operatörü çıktıyı dosyaya alır ama ekrana hiçbir şey basmaz. İşte tee komutu tam bu boşluğu doldurur: adını borulardaki "T bağlantısından" alır, gelen akışı ikiye çatallayarak bir kopyasını dosyaya yazar, diğerini stdout'tan (ekrana veya sonraki komuta) geçirir.
# Çıktıyı hem ekranda gör hem log dosyasına yaz
./kurulum.sh | tee kurulum.log
# Dosyanın üzerine yazmak yerine sonuna ekle
./kurulum.sh | tee -a kurulum.log
tee'nin en değerli olduğu an, yetki gerektiren dosyalara yazma anıdır. Yeni başlayanların sık düştüğü bir tuzak vardır: sudo echo "satir" > /etc/ayar.conf yazarlar ve "izin reddedildi" hatası alırlar. Sebebi ustaca gizlidir. sudo yalnızca echo komutunu root olarak çalıştırır; ama > yönlendirmesini yapan kabuğun kendisidir ve o hâlâ sizin normal yetkinizle çalışır. Çözüm, yazma işini root olarak çalışan tee'ye devretmektir:
# Bu çalışmaz: yönlendirmeyi kabuk yapar, sudo'yu göremez
sudo echo "net.ipv4.ip_forward=1" > /etc/sysctl.conf # İzin reddedildi
# Bu çalışır: tee root olarak yazar
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
tee çıktısını hem dosyaya yazıp hem de zincire devam ettirdiği için, uzun bir zincirin ortasında ara sonucu kaydetmek için de idealdir:
# Ara adımı ham.log'a kaydet, sonra filtreleyip say
cat erisim.log | tee ham.log | grep '404' | wc -l
Gerçek Dünya Örnekleri: Log Analizi ve Komut Zincirleri#
Şimdi öğrendiklerimizi bir sistem yöneticisinin masasında gerçekten karşılaşacağı işlere uygulayalım. Diyelim ki web sunucunuzun erişim logunda anormal bir trafik var ve saldırganı bulmak istiyorsunuz. Nginx erişim logunda en çok istek yapan ilk on IP adresini şöyle çıkarırsınız:
awk '{print $1}' /var/log/nginx/access.log \
| sort \
| uniq -c \
| sort -rn \
| head -n 10
Bu zincir okumaya değer. awk '{print $1}' her satırın ilk alanını (IP) alır; sort aynı IP'leri yan yana getirir; uniq -c her benzersiz IP'nin kaç kez tekrarladığını sayar; ikinci sort -rn en sık gelenleri başa taşır; head ilk onu gösterir. Beş küçük komut, tek satırda bir trafik analiz aracına dönüşür.
Bir başka sık iş: bir hatanın loglarda ne zaman ve kaç kez göründüğünü bulmak. Aşağıdaki komut bugünün tarihini içeren "error" satırlarını sayar ve aynı zamanda bir dosyaya döker:
grep "$(date +%Y-%m-%d)" /var/log/app/app.log \
| grep -i "error" \
| tee bugun-hatalar.log \
| wc -l
Canlı akan bir logu gerçek zamanlı takip ederken filtrelemek de çok yaygındır. tail -f logu canlı izler, pipe ile grep'e verip yalnızca ilgilendiğiniz satırları görürsünüz:
# Canlı logda sadece 500 hatalarını izle
tail -f /var/log/nginx/error.log | grep "500"
Bir veritabanı yedeğini sıkıştırıp doğrudan dosyaya yazmak, pipe ve yönlendirmenin birlikte parladığı klasik bir örnektir. Burada mysqldump'ın çıktısı ara dosyaya hiç inmeden gzip'e akar, sonra > ile sıkıştırılmış dosyaya yazılır:
mysqldump -u root -p veritabani | gzip > yedek-$(date +%F).sql.gz
Aynı mantığı ters yönde, bir dosyayı komuta girdi olarak vermek için < operatörüyle kullanırız. Örneğin bir SQL dosyasını MySQL'e beslemek:
mysql -u root -p veritabani < yedek.sql
Bir liste dosyasındaki her satır için işlem yapmak da sık gerekir. xargs, pipe'tan gelen değerleri bir komutun argümanlarına çevirir:
# Bir metin dosyasındaki her paket adını tek komutta kur
cat paketler.txt | xargs apt-get install -y
Bu tür tek satırlık komutları düzenli olarak kuruyor ve kaydediyorsanız, onları küçük bir betiğe dönüştürmenin zamanı gelmiş demektir. Betiği bir metin düzenleyiciyle yazmak için nano ve vim rehberimize göz atabilir, komut zincirlerinizi tekrar tekrar elle yazmak yerine dosyaya kaydedip çalıştırabilirsiniz.
Sık Yapılan Hatalar ve Küçük Püf Noktaları#
Yönlendirme ve pipe konusunda başlangıçta herkesin takıldığı birkaç nokta vardır; bunları baştan bilmek saatlerce sürecek şaşkınlıktan kurtarır.
En yaygın yanılgı, > ile bir dosyanın üzerine kazara yazmaktır. command > dosya.log her çalıştığında dosyayı sıfırlar; log biriktirmek istiyorsanız neredeyse her zaman >> kullanmanız gerekir. İkinci sık hata, hata mesajlarını yakalayacağını sanıp > cikti.txt yazmak ama hataların hâlâ ekranda kalmasıdır; hatayı da almak için 2> veya &> gerekir.
Bir başka ince tuzak, aynı dosyayı hem girdi hem çıktı olarak kullanmaktır. sort dosya.txt > dosya.txt komutu dosyayı boşaltır, çünkü kabuk komut çalışmadan önce > ile dosyayı sıfırlar. Bir dosyayı kendi üzerine işlemek istiyorsanız sort dosya.txt -o dosya.txt gibi komutun kendi güvenli seçeneğini kullanın ya da geçici bir dosyaya yazıp sonra taşıyın.
Son olarak grep ile pipe kullanırken renkli çıktının bir sonraki komuta gizli kontrol karakterleri taşıdığını unutmayın; grep --color=never ya da düz çıktı üreten seçenekler zincirin geri kalanını temiz tutar. Bu tür ayrıntılar, komut satırında ustalaşmanın kendisidir. Sunucunuzda bu komutları rahatça deneyebileceğiniz bir ortam istiyorsanız, tam yetkiye sahip olduğunuz bir VDS sunucu ideal bir laboratuvardır; yönetimini bize bırakmak isterseniz sunucu yönetimi hizmetimiz de devrede.
Sıkça Sorulan Sorular#
> ile >> arasındaki fark nedir?#
Tek ok > hedef dosyanın mevcut içeriğini tamamen siler ve yerine yeni çıktıyı yazar. Çift ok >> ise dosyayı silmeden çıktıyı sonuna ekler. Log biriktirmek, bir dosyaya zaman içinde satır satır not düşmek istiyorsanız >> kullanın; tek seferlik bir raporu sıfırdan oluşturuyorsanız > yeterlidir.
stdout ve stderr neden ayrı iki kanaldır?#
Programların normal sonuçlarıyla hata mesajlarını birbirinden ayırabilmek için ayrı tasarlanmışlardır. Bu ayrım sayesinde bir komutun ürettiği veriyi bir dosyaya veya sonraki komuta gönderirken, hata ve uyarıları başka bir yere yönlendirebilir ya da tamamen susturabilirsiniz. İkisi de varsayılan olarak ekrana düştüğü için karışık görünürler; ayrı kanal olmaları, 2> gibi operatörlerle onları bağımsızca yönetmeyi mümkün kılar.
Bir komutun hata mesajlarını nasıl tamamen gizlerim?#
Hata akışını Linux'un kara deliği olan /dev/null dosyasına yönlendirmeniz yeterlidir. Örneğin find / -name "*.log" 2> /dev/null komutu erişim izni olmayan dizinlere dair uyarıları görünmez kılar. Hem çıktıyı hem hatayı birlikte susturmak isterseniz komut &> /dev/null yazabilirsiniz. Bu yöntem özellikle betiklerde gürültüyü azaltmak için çok kullanılır.
sudo echo ... > /etc/dosya neden izin reddedildi hatası veriyor?#
Çünkü sudo yalnızca echo komutunu root olarak çalıştırır, ancak > yönlendirmesini yapan kabuğun kendisidir ve o hâlâ sizin sıradan yetkinizle çalışır. Yani yazma işlemi root yetkisi almadığı için başarısız olur. Doğru çözüm yazmayı root olarak çalışan tee komutuna devretmektir: echo "satir" | sudo tee -a /etc/dosya biçiminde kullanın.
Pipe zincirimin ortasındaki bir komut hata verirse ne olur?#
Zincirdeki her komut bir öncekinin çıktısını girdi olarak aldığından, ortadaki bir komut boş çıktı üretir veya hata verirse sonraki komutlar boş girdiyle çalışır ve genellikle beklediğiniz sonucu vermez. Sorunu bulmanın en pratik yolu, zinciri baştan kurup komutları teker teker ekleyerek çalıştırmak ve çıktının hangi adımda bozulduğunu gözlemlemektir. Böylece hatalı halkayı hızla tespit edersiniz.
tee komutunu ne zaman > yerine tercih etmeliyim?#
Çıktıyı bir dosyaya kaydederken aynı anda ekranda da görmek istediğinizde tee kullanın; > çıktıyı yalnızca dosyaya alır ve ekrana hiçbir şey basmaz. Uzun süren bir kurulum betiğinin çıktısını hem izleyip hem kaydetmek, bir pipe zincirinin ortasında ara sonucu saklamak veya root yetkisi gereken dosyalara sudo ile yazmak tee'nin parladığı durumlardır. Dosyanın sonuna eklemek için tee -a seçeneğini kullanmayı unutmayın.
Kapanış#
Pipe ve yönlendirme, ilk bakışta birkaç garip işaretten ibaret görünse de aslında Linux'un tüm gücünü avucunuza veren anahtardır. stdin, stdout ve stderr üçlüsünü kavradığınızda; >, >>, 2>, &> operatörlerini ve | ile tee'yi bir araya getirdiğinizde, tek satırlık komutlarla saatler sürecek işleri saniyeler içinde halledebilirsiniz. Bu becerileri gerçekten oturtmanın tek yolu, kendi sunucunuzda korkusuzca denemektir: log dosyalarını süzün, süreçleri sıralayın, çıktıları kaydedin ve zincirler kurun.
Clou.TR olarak bu komutları rahatça deneyebileceğiniz güçlü altyapılar sunuyoruz. Tam root yetkisiyle özgürce çalışmak isteyenler için VDS ve sanal sunucu paketlerimize, sunucu bakımını uzmanlara bırakmak isteyenler için sunucu yönetimi hizmetimize, hızlı başlamak isteyenler için ise hosting çözümlerimize göz atabilirsiniz. Öğrenmeye devam etmek isterseniz Bilgi Merkezi rehberlerimiz ve pratik araçlarımız sizi bekliyor.