Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Hataları Standart Hata Çıktısına Yönlendirme

Şu anda println! makrosunu kullanarak tüm çıktılarımızı terminale yazıyoruz. Çoğu terminalde iki tür çıktı akışı bulunur: Genel bilgiler için standart çıktı (standard output, stdout) ve hata mesajları için standart hata (standard error, stderr). Bu ayrım sayesinde kullanıcılar, bir programın başarılı çıktısını bir dosyaya yönlendirirken hata mesajlarının yine de ekrana yazdırılmasını sağlayabilir.

println! makrosu yalnızca standart çıktıya yazdırabilir; dolayısıyla standart hataya yazdırmak için başka bir araç kullanmamız gerekir.

Hataların Nereye Yazıldığını Kontrol Etme

İlk olarak, standart hataya yazdırmak istediğimiz hata mesajları da dahil olmak üzere minigrep tarafından üretilen tüm çıktının şu anda standart çıktıya nasıl yazıldığını gözlemleyelim. Bunu görmek için bilerek bir hataya yol açarken standart çıktı akışını bir dosyaya yönlendireceğiz. Standart hata akışını yönlendirmeyeceğimiz için standart hataya gönderilen her türlü içerik ekranda görünmeye devam edecektir.

Komut satırı programlarının hata mesajlarını standart hata akışına göndermesi beklenir; böylece standart çıktı akışını bir dosyaya yönlendirsek bile hata mesajlarını ekranda görmeye devam edebiliriz. Programımız şu anda bu kurala uygun davranmıyor: Birazdan hata mesajı çıktısını ekrana basmak yerine dosyaya kaydettiğini göreceğiz!

Bu davranışı göstermek için programı > operatörü ve standart çıktı akışını yönlendirmek istediğimiz cikti.txt dosya yoluyla çalıştıracağız. Hataya yol açması için hiçbir argüman iletmeyeceğiz:

$ cargo run > cikti.txt

> sözdizimi, kabuğa standart çıktının içeriğini ekran yerine cikti.txt dosyasına yazmasını söyler. Ekranda görmeyi beklediğimiz hata mesajıyla karşılaşmadık; demek ki mesaj dosyaya yazılmış olmalı. cikti.txt dosyasının içeriği şöyledir:

Argümanları ayrıştırırken problem oluştu: yeterli argüman yok

Gerçekten de hata mesajımız standart çıktıya yazdırılıyor. Bu tür hata mesajlarının standart hataya yazdırılması çok daha kullanışlıdır; böylece dosyaya yalnızca başarılı bir çalıştırmadan elde edilen veriler kaydedilir. Şimdi bunu değiştireceğiz.

Hataları Standart Hata Çıktısına Yazdırma

Hata mesajlarının yazdırılma biçimini değiştirmek için Liste 12-24’teki kodu kullanacağız. Bu bölümün başlarında yaptığımız yeniden düzenleme sayesinde, hata mesajlarını yazdıran tüm kodlar tek bir fonksiyonda, yani main içinde toplanmış durumda. Standart kütüphane, standart hata akışına yazdıran eprintln! makrosunu sunar; dolayısıyla hata yazdırmak için println! çağırdığımız iki yeri eprintln! kullanacak şekilde değiştirelim.

Dosya adı: src/main.rs
use std::env;
use std::error::Error;
use std::fs;
use std::process;

use minigrep::{ara, buyuk_kucuk_harf_duyarsiz_ara};

fn main() {
    let argumanlar: Vec<String> = env::args().collect();

    let yapilandirma =
        Yapilandirma::olustur(&argumanlar).unwrap_or_else(|hata| {
            eprintln!("Argümanları ayrıştırırken problem oluştu: {hata}");
            process::exit(1);
        });

    if let Err(e) = calistir(yapilandirma) {
        eprintln!("Uygulama hatası: {e}");
        process::exit(1);
    }
}

pub struct Yapilandirma {
    pub sorgu: String,
    pub dosya_yolu: String,
    pub buyuk_kucuk_harf_yoksay: bool,
}

impl Yapilandirma {
    fn olustur(argumanlar: &[String]) -> Result<Yapilandirma, &'static str> {
        if argumanlar.len() < 3 {
            return Err("yeterli argüman yok");
        }

        let sorgu = argumanlar[1].clone();
        let dosya_yolu = argumanlar[2].clone();

        let buyuk_kucuk_harf_yoksay = env::var("IGNORE_CASE").is_ok();

        Ok(Yapilandirma {
            sorgu,
            dosya_yolu,
            buyuk_kucuk_harf_yoksay,
        })
    }
}

fn calistir(yapilandirma: Yapilandirma) -> Result<(), Box<dyn Error>> {
    let icerik = fs::read_to_string(yapilandirma.dosya_yolu)?;

    let sonuclar = if yapilandirma.buyuk_kucuk_harf_yoksay {
        buyuk_kucuk_harf_duyarsiz_ara(&yapilandirma.sorgu, &icerik)
    } else {
        ara(&yapilandirma.sorgu, &icerik)
    };

    for satir in sonuclar {
        println!("{satir}");
    }

    Ok(())
}
Liste 12-24: eprintln! kullanarak hata mesajlarını standart çıktı yerine standart hataya yazdırma

Şimdi programı hiçbir argüman vermeden ve standart çıktıyı > ile yönlendirerek aynı şekilde tekrar çalıştıralım:

$ cargo run > cikti.txt
Argümanları ayrıştırırken problem oluştu: yeterli argüman yok

Artık hatayı ekranda görüyoruz ve cikti.txt dosyası hiçbir şey içermiyor; komut satırı programlarından beklenen davranış da tam olarak budur.

Şimdi programı hataya yol açmayan argümanlarla tekrar çalıştıralım, ancak standart çıktıyı yine aşağıdaki gibi bir dosyaya yönlendirelim:

$ cargo run -- ne siir.txt > cikti.txt

Terminalde herhangi bir çıktı görmeyeceğiz ve cikti.txt dosyası arama sonuçlarımızı barındıracak:

Dosya adı: cikti.txt

Gelme, artık neye yarar?

Bu sonuç, artık başarılı çıktılar için standart çıktıyı, hata mesajları içinse standart hatayı olması gerektiği gibi kullandığımızı gösterir.

Not: Unix türevi sistemlerde (Linux, macOS) ve modern Windows kabuklarında başlatılan her sürece otomatik olarak numaralandırılmış üç standart akış (dosya tanıtıcısı, file descriptor) atanır:

  • 0 — stdin (standart girdi): Klavyeden veya başka bir süreçten gelen veriyi okur (std::io::stdin()).
  • 1 — stdout (standart çıktı): Programın ürettiği normal sonuçları taşır (print!, println!).
  • 2 — stderr (standart hata): Hata, uyarı ve tanılama mesajlarını taşır (eprint!, eprintln!, dbg!).

Kabukta yazdığınız > operatörü aslında 1> ifadesinin kısaltmasıdır ve yalnızca stdout akışını yönlendirir. Yalnızca hata mesajlarını bir dosyaya kaydetmek isterseniz cargo run 2> hatalar.txt, hem normal çıktıyı hem de hata mesajlarını aynı dosyada birleştirmek isterseniz cargo run > tum_cikti.txt 2>&1 sözdizimini kullanabilirsiniz.

eprintln! kullanımı özellikle borulama (piping, |) işlemlerinde hayati önem taşır. Örneğin eşleşen satır sayısını bulmak için minigrep ne siir.txt | wc -l gibi bir komut zinciri kurduğunuzda kabuk, birinci programın yalnızca stdout (1) akışını ikinci programın stdin (0) akışına bağlar. Hataları println! ile stdout’a yazarsanız sonraki program hata metnini gerçek veri sanıp yanlış sonuç üretir (örneğin hata mesajının satırını da sayar!) ya da ayrıştırma hatasıyla çöker; eprintln! kullandığınızda ise hata mesajları boruya karışmadan doğrudan ekranda görünür ve sonraki araca yalnızca temiz veri aktarılır.

Özet

Bu bölümde, şimdiye kadar öğrendiğiniz pek çok temel kavramı yeniden hatırladık ve Rust’ta yaygın G/Ç işlemlerinin nasıl gerçekleştirileceğini ele aldık. Komut satırı argümanlarını, dosyaları, çevre değişkenlerini ve hata yazdırmaya yarayan eprintln! makrosunu kullanarak artık komut satırı uygulamaları geliştirmeye hazırsınız. Bunları önceki bölümlerdeki kavramlarla bir araya getirdiğinizde kodunuz iyi organize edilmiş ve iyi test edilmiş olacak; verileri uygun veri yapılarında verimli biçimde saklayıp hataları düzgünce yönetecektir.

Bir sonraki bölümde fonksiyonel dillerden esinlenen bazı Rust özelliklerini inceleyeceğiz: kapanışlar ve iteratörler.