Rust'ta komut satırı argümanlarını std::env::args() ile, ortam değişkenlerini std::env::var("AD") ile okursunuz. args() bir yineleyici döndürür ve ilk elemanı geleneksel olarak programın kendi yoludur; kullanıcının verdiği argümanlar ikinci elemandan başlar. Hata mesajlarını println! yerine eprintln! ile standart hataya yazar, programı başarısız bir çıkış koduyla bitirmek için de main fonksiyonundan ExitCode döndürür ya da std::process::exit çağırırsınız.
Bu yazı, The Rust Book'un 12. bölümündeki küçük arama aracı projesinin izinden gider, ama yalnızca komut satırı tarafına odaklanır: argümanları okuma, doğrulama, yapılandırma nesnesine çevirme, ortam değişkenleri, çıktı akışları ve çıkış kodları. Dosya okuma ayrıntıları için std::fs ile dosya okuma ve yazma yazısına bakabilirsiniz. Hazır argüman ayrıştırma kütüphaneleri resmî belgelerin kapsamında olmadığı için burada ele alınmıyor; bunlara docs.rs üzerinden ulaşabilirsiniz.
env::args ile argümanları okumak

std::env::args fonksiyonu Args tipinde bir yineleyici döndürür ve her argüman bir String olarak gelir. En yaygın kullanım, değerleri collect ile bir vektöre toplamaktır. collect pek çok koleksiyon türü üretebildiği için hedef tipi, örneğin Vec<String>, açıkça yazmanız gerekir; Rust bunu kendisi çıkaramaz.
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
println!("toplam: {}", args.len());
for (i, a) in args.iter().enumerate().skip(1) {
println!("{i}: {a}");
}
}$ ./argumanlar ara "iki kelime" 'ç*'
toplam: 4
1: ara
2: iki kelime
3: ç*Tırnak içindeki iki kelime tek argüman olarak geldi, çünkü argümanları bölen program değil kabuktur. Belgeler burada iki önemli ayrıntıya dikkat çeker. Birincisi, ilk elemanın program yolu olması bir gelenektir; bu değer keyfi bir metne ayarlanabilir, hatta hiç olmayabilir. Bu yüzden güvenlikle ilgili kararlarda ona güvenmemelisiniz. İkincisi, Unix sistemlerde kabuk tırnaksız * ve ? desenlerini genellikle dosya adlarına genişletir, Windows'ta ise bu yapılmaz ve argüman olduğu gibi geçer. Yukarıdaki örnekte tek tırnak kullanıldığı için desen genişletilmedi.
Cargo ile çalışırken argümanları programınıza iletmek için iki tire kullanılır: cargo run -- ara dosya.txt. İki tireden önceki her şey Cargo'ya, sonrası programınıza aittir. Kitap, fonksiyonu use std::env::args ile doğrudan içe aktarmak yerine üst modülü içe aktarmayı önerir; böylece kodda env::args() yazdığınızda fonksiyonun nereden geldiği açıkça görünür ve aynı modüldeki başka fonksiyonları da kolayca kullanırsınız.
Geçersiz Unicode ve args_os
env::args() yineleyicisi, argümanlardan biri geçerli Unicode değilse yineleme sırasında panik oluşturur. Dosya adlarını argüman olarak alan araçlarda bu gerçek bir ihtimaldir, çünkü işletim sistemleri yollarda Unicode olmayan baytlara izin verebilir. Böyle bir durumda env::args_os() kullanın: String yerine OsString üretir ve panik oluşturmaz. Kitap, OsString değerlerinin platforma göre farklılaştığını ve bu yüzden String ile çalışmaktan daha zahmetli olduğunu belirtir; basit araçlarda args() bu nedenle tercih edilir.
Argümanları bir yapılandırma tipine dönüştürmek
args[1] gibi doğrudan indeksleme, argüman eksikse index out of bounds paniğine yol açar. Bu mesaj programcıya yöneliktir, son kullanıcıya bir şey anlatmaz. Kitap bu sorunu adım adım çözer: önce kontrol ekleyip panic! ile daha iyi bir mesaj verir, sonra bunun da yanlış araç olduğunu söyler. Panik programlama hataları içindir; eksik argüman ise bir kullanım hatasıdır. Doğru yaklaşım, doğrulamayı yapan fonksiyonun Result döndürmesidir. Kitap ayrıca bu fonksiyona new yerine build adını verir, çünkü pek çok programcı new fonksiyonlarının başarısız olmamasını bekler.
use std::env;
use std::process;
struct Ayar {
aranan: String,
dosya: String,
}
impl Ayar {
fn kur(args: &[String]) -> Result<Ayar, &'static str> {
if args.len() < 3 {
return Err("eksik argüman: aranan ve dosya gerekli");
}
Ok(Ayar { aranan: args[1].clone(), dosya: args[2].clone() })
}
}
fn main() {
let args: Vec<String> = env::args().collect();
let ayar = Ayar::kur(&args).unwrap_or_else(|hata| {
eprintln!("Argümanlar okunamadı: {hata}");
process::exit(2);
});
println!("aranan: {}", ayar.aranan);
println!("dosya: {}", ayar.dosya);
}$ ./ayar
Argümanlar okunamadı: eksik argüman: aranan ve dosya gerekli
$ echo $?
2
$ ./ayar merhaba not.txt
aranan: merhaba
dosya: not.txtunwrap_or_else, Ok durumunda içteki değeri verir; Err durumunda ise verdiğiniz closure'ı hatayla birlikte çalıştırır. Closure içinde mesajı yazıp process::exit çağırdığımız için panik çıktısındaki iş parçacığı adı ve RUST_BACKTRACE notu kullanıcıya gösterilmez. Closure'ların nasıl çalıştığı closure yazısında anlatılıyor.
Bu sürümdeki clone() çağrıları küçük bir maliyettir. Kitap, yineleyiciler bölümünde aynı fonksiyonu env::args() yineleyicisinin sahipliğini alacak şekilde yeniden yazar; böylece kopyalamaya gerek kalmaz. Aşağıdaki mini örnekte bu biçimi kullanacağız.
Ortam değişkenlerini okumak
std::env::var bir anahtar alır ve Result<String, VarError> döndürür. Değişken tanımlı değilse ya da adında = veya NUL karakteri varsa VarError::NotPresent, değeri geçerli Unicode değilse VarError::NotUnicode gelir. Değişkenin yalnızca tanımlı olup olmadığı önemliyse is_ok() yeterlidir; kitaptaki aracın büyük-küçük harf duyarsız arama seçeneği tam olarak böyle çalışır.
use std::env;
fn main() {
let buyuk_kucuk = env::var("IGNORE_CASE").is_ok();
println!("IGNORE_CASE tanımlı mı: {buyuk_kucuk}");
match env::var("UYGULAMA_PORT") {
Ok(deger) => println!("port: {deger}"),
Err(e) => println!("UYGULAMA_PORT: {e}"),
}
let port: u16 = env::var("UYGULAMA_PORT")
.ok()
.and_then(|d| d.parse().ok())
.unwrap_or(8080);
println!("kullanılacak port: {port}");
}$ ./ortam
IGNORE_CASE tanımlı mı: false
UYGULAMA_PORT: environment variable not found
kullanılacak port: 8080
$ IGNORE_CASE=1 UYGULAMA_PORT=3000 ./ortam
IGNORE_CASE tanımlı mı: true
port: 3000
kullanılacak port: 3000
$ UYGULAMA_PORT=abc ./ortam
IGNORE_CASE tanımlı mı: false
port: abc
kullanılacak port: 8080Üçüncü çalıştırma bir tasarım kararını gösteriyor: abc sayıya çevrilemediği için sessizce varsayılan değere düştük. Bu bazen istenir, bazen kullanıcının yazım hatasını gizler. Hatalı değerde uyarı vermek istiyorsanız parse sonucunu match ile ayrıca ele alın. Unicode olmayan değerleri kayıpsız almak için env::var_os vardır ve Option<OsString> döndürür. Değişkeni program çalışırken değil derleme anında okumak istiyorsanız belgeler env! ve option_env! makrolarını önerir.
Tüm ortam değişkenlerini gezmek için env::vars() kullanılır; (anahtar, değer) çiftleri üreten bir yineleyici döndürür. Belgeye göre bu yineleyici çağrı anındaki ortamın bir anlık görüntüsünü tutar, sonradan yapılan değişiklikleri yansıtmaz ve herhangi bir anahtar ya da değer geçerli Unicode değilse yineleme sırasında panik oluşturur; bu durumda env::vars_os() tercih edilir. Aynı modülde komut satırı araçlarında sık gereken iki fonksiyon daha vardır: env::current_dir() çalışma klasörünü io::Result<PathBuf> olarak verir ve klasör silinmişse hata döndürebilir; env::current_exe() ise çalışan programın tam yolunu döndürür. Göreli bir dosya yolu argümanı, programın bulunduğu klasöre değil çalışma klasörüne göre çözülür; bu fark, araç başka bir klasörden çağrıldığında şaşırtıcı sonuçlara yol açabilir.
Standart çıktı ve standart hata
Terminallerde iki çıktı akışı vardır: genel çıktı için standart çıktı (stdout), hata mesajları için standart hata (stderr). Bu ayrım sayesinde kullanıcı başarılı çıktıyı > ile bir dosyaya yönlendirirken hata mesajlarını ekranda görmeye devam eder. println! yalnızca standart çıktıya yazar; standart hata için eprintln! kullanılır. Belgeler eprintln! makrosunun yalnızca hata ve ilerleme mesajları için, programın asıl çıktısı için ise println! kullanılmasını önerir. Ayrıca stderr'e yazma başarısız olursa eprintln! makrosunun panik oluşturduğunu belirtir.
Kitaptaki deneme bunu iyi gösterir: hata mesajları println! ile yazıldığında cargo run > output.txt komutu ekranda hiçbir şey göstermez, hata mesajı dosyaya gider. Mesajlar eprintln! ile yazılınca hata ekranda kalır, dosyaya yalnızca gerçek sonuçlar düşer. Aşağıdaki mini örnekte bulunan satır sayısını bilerek standart hataya yazdık; yönlendirme yapıldığında bu bilgi ekranda kalırken sonuç satırları dosyaya gider.
Çıkış kodları: process::exit, ExitCode ve Result

Sıfır olmayan bir çıkış kodu, programı çağıran sürece bir hata olduğunu bildirmenin geleneksel yoludur. Rust'ta bunun üç yolu vardır. std::process::exit süreci hemen sonlandırır ve hiç geri dönmez; dönüş tipi ! olduğu için unwrap_or_else closure'ı içinde rahatça kullanılabilir. Ancak belgeler önemli bir yan etkiyi vurgular: mevcut yığındaki ve diğer iş parçacıklarındaki yıkıcılar (Drop) çalıştırılmaz.
struct Temizlik;
impl Drop for Temizlik {
fn drop(&mut self) {
println!("geçici dosyalar silindi");
}
}
fn main() {
let _t = Temizlik;
println!("çalışıyor");
if std::env::args().count() > 1 {
std::process::exit(1);
}
}
// argümansız çalıştırınca çıktı:
// çalışıyor
// geçici dosyalar silindi
// bir argümanla çalıştırınca çıktı:
// çalışıyorBu yüzden belgeler temiz bir kapanış gerekiyorsa exit çağrısını yıkıcı kalmayan bir noktada yapmayı ya da daha iyisi main fonksiyonundan Termination trait'ini uygulayan bir tip döndürmeyi önerir. Bu tiplerden biri ExitCode tipidir: ExitCode::SUCCESS, ExitCode::FAILURE ve ExitCode::from(u8) ile kod üretirsiniz. main normal biçimde döndüğü için yerel değişkenlerin yıkıcıları çalışır.
use std::env;
use std::process::ExitCode;
fn main() -> ExitCode {
let sayi = env::args().nth(1);
let Some(metin) = sayi else {
eprintln!("kullanım: ikikat <sayı>");
return ExitCode::from(2);
};
match metin.parse::<i64>() {
Ok(n) => {
println!("iki katı: {}", n * 2);
ExitCode::SUCCESS
}
Err(e) => {
eprintln!("'{metin}' sayı değil: {e}");
ExitCode::FAILURE
}
}
}$ ./ikikat 21
iki katı: 42
$ ./ikikat on
'on' sayı değil: invalid digit found in string
$ echo $?
1
$ ./ikikat
kullanım: ikikat <sayı>
$ echo $?
2Üçüncü yol main fonksiyonunun Result<(), E> döndürmesidir; burada E tipinin Debug uygulaması olmalıdır. Err döndüğünde standart kütüphane hatayı Debug biçimiyle standart hataya yazar ve başarısız kodla çıkar. Bu, hızlı araçlar için pratiktir ama mesaj biçimi üzerinde kontrolünüz azalır; örneğin metin tırnak içinde görünür:
fn main() -> Result<(), String> {
let ilk = std::env::args().nth(1).ok_or("argüman yok")?;
println!("ilk argüman: {ilk}");
Ok(())
}
// argümansız çalıştırınca standart hataya yazılan:
// Error: "argüman yok"
// çıkış kodu: 1Belgeler ayrıca çıkış kodlarının taşınabilir olmadığını hatırlatır. Unix benzeri sistemlerin çoğunda üst sürece yalnızca en düşük sekiz bit görünür; belgedeki örneğe göre process::exit(0x0100) Linux'ta 0, Windows'ta 256 koduyla sonuçlanır. ExitCode yalnızca u8 kabul ederek bu sorunu büyük ölçüde önler.
| Yöntem | Yıkıcılar çalışır mı? | Ne zaman uygun? |
|---|---|---|
| process::exit(kod) | Hayır | Closure içinden hemen çıkmak, temizlenecek bir şey yoksa |
| main -> ExitCode | Evet | Farklı hata kodları vermek istediğinizde |
| main -> Result<(), E> | Evet | Hızlı araçlar; hata Debug biçimiyle yazılır |
| panic! | Varsayılan ayarda evet | Programlama hataları; kullanıcı hataları için değil |
main.rs ile lib.rs ayrımı
Kitabın bu projede önerdiği yapı, komut satırı programları için iyi bir varsayılandır. Program büyüdükçe mantık src/lib.rs dosyasına taşınır, main fonksiyonunda yalnızca şu işler kalır: argümanlarla ayrıştırma mantığını çağırmak, diğer yapılandırmayı kurmak, kütüphanedeki run fonksiyonunu çağırmak ve run hata döndürürse bunu ele almak. main doğrudan test edilemediği için bu ayrım, programın asıl mantığını birim testleriyle sınamayı mümkün kılar. Modül ve crate yapısının ayrıntıları modül sistemi yazısında, testler ise cargo test yazısında anlatılıyor.
Uygulamalı mini örnek: küçük bir arama aracı
Aşağıdaki program bir kelimeyi bir dosyada arar, eşleşen satırları standart çıktıya, bulunan satır sayısını standart hataya yazar. IGNORE_CASE ortam değişkeni tanımlıysa büyük-küçük harfe bakmadan arar. Yapılandırma fonksiyonu yineleyicinin sahipliğini alır, eksik argümanda 2, çalışma hatasında 1 koduyla çıkar.
use std::env;
use std::error::Error;
use std::fs;
use std::process;
struct Ayar {
aranan: String,
dosya: String,
harf_duyarsiz: bool,
}
impl Ayar {
fn kur(mut args: impl Iterator<Item = String>) -> Result<Ayar, &'static str> {
args.next(); // program adı
let aranan = args.next().ok_or("aranan kelime eksik")?;
let dosya = args.next().ok_or("dosya yolu eksik")?;
let harf_duyarsiz = env::var("IGNORE_CASE").is_ok();
Ok(Ayar { aranan, dosya, harf_duyarsiz })
}
}
fn calistir(ayar: &Ayar) -> Result<usize, Box<dyn Error>> {
let icerik = fs::read_to_string(&ayar.dosya)?;
let aranan = if ayar.harf_duyarsiz { ayar.aranan.to_lowercase() } else { ayar.aranan.clone() };
let mut bulunan = 0;
for satir in icerik.lines() {
let s = if ayar.harf_duyarsiz { satir.to_lowercase() } else { satir.to_string() };
if s.contains(&aranan) {
println!("{satir}");
bulunan += 1;
}
}
Ok(bulunan)
}
fn main() {
let ayar = Ayar::kur(env::args()).unwrap_or_else(|e| {
eprintln!("hata: {e}");
process::exit(2);
});
match calistir(&ayar) {
Ok(n) => eprintln!("{n} satır bulundu"),
Err(e) => {
eprintln!("uygulama hatası: {e}");
process::exit(1);
}
}
}$ printf 'Rust ile dosya\nrust kurulumu\nPython notları\nRUST sözlüğü\n' > notlar.txt
$ ./ara rust notlar.txt
rust kurulumu
1 satır bulundu
$ IGNORE_CASE=1 ./ara rust notlar.txt
Rust ile dosya
rust kurulumu
RUST sözlüğü
3 satır bulundu
$ ./ara rust
hata: dosya yolu eksik
$ echo $?
2
$ ./ara rust notlar.txt > sonuc.txt
1 satır bulundu
$ cat sonuc.txt
rust kurulumuSon iki komut, stdout ile stderr ayrımının pratik faydasını gösteriyor: sonuç dosyaya gitti, bilgi mesajı ekranda kaldı. calistir fonksiyonunun dönüş tipi Box<dyn Error> olduğu için içerideki ? hem io::Error hem de ileride ekleyeceğiniz başka hata tiplerini taşıyabilir. Bu tipin nasıl çalıştığı Error trait yazısında ayrıntılı anlatılıyor. Örnekler Windows'ta Git Bash içinde derlenip çalıştırıldı; Windows'ta program dosyasının adı ara.exe olur.
Sık yapılan hatalar
![Sık yapılan hatalar: Kullanıcı argümanlarını args[0] konumundan başlatmak; ilk eleman geleneksel..., args[1] ile doğrudan indeksleyip eksik argümanda kullanıcıya anlaşılmaz...](/assets/blog/rust-komut-satiri-argumanlari-env-b9.webp)
- Kullanıcı argümanlarını
args[0]konumundan başlatmak; ilk eleman geleneksel olarak program yoludur. args[1]ile doğrudan indeksleyip eksik argümanda kullanıcıya anlaşılmaz bir panik göstermek.- Hata mesajlarını
println!ile yazmak; yönlendirmede mesajlar dosyaya karışır. process::exitçağrısının yıkıcıları çalıştırmadığını unutup tamponlu yazıcıları boşaltmadan çıkmak.- Dosya adı alan bir araçta
args()kullanıp Unicode olmayan adlarda panik yaşamak;args_os()bu durumda daha güvenlidir. - Çok iş parçacıklı bir programda ortam değişkenini
set_varile değiştirmek. collect()sonucunun tipini belirtmeyip derleyicinin tip çıkaramadığı hatasını almak.
Sık Sorulan Sorular
cargo run ile argüman nasıl veririm?
İki tireden sonra yazarsınız: cargo run -- kelime dosya.txt. İki tireden önceki bayraklar Cargo'ya, sonrakiler programınıza gider. Cargo ile proje kurulumu için kurulum yazısına bakabilirsiniz.
Bayrakları (--yardim gibi) nasıl ayrıştırırım?
Standart kütüphanede hazır bir bayrak ayrıştırıcı yoktur. Küçük araçlarda argümanları yineleyip match ile karşılaştırmak yeterlidir. Daha karmaşık arayüzler için topluluk kütüphanelerine docs.rs üzerinden bakabilirsiniz; bunlar bu yazının kapsamı dışında.
Çıkış kodu olarak hangi değeri kullanmalıyım?
Başarı için ExitCode::SUCCESS, genel başarısızlık için ExitCode::FAILURE en taşınabilir seçeneklerdir. Farklı hata türlerini ayırt etmek istiyorsanız ExitCode::from ile küçük sayılar verin; belgeler sayısal değerlerin platformlar arasında taşınabilir anlamı olmadığını hatırlatır.
Ortam değişkeni ile argüman arasında nasıl seçim yaparım?
Kitap bu seçimi şöyle gerekçelendirir: bir seçeneği komut satırı argümanı yaparsanız kullanıcı onu her çalıştırmada yazmak zorunda kalır; ortam değişkeni yaparsanız bir kez ayarlar ve o terminal oturumundaki tüm çalıştırmalar bundan etkilenir. Kitap ayrıca bazı programların aynı ayarı hem argümanla hem ortam değişkeniyle kabul ettiğini ve bu durumda birinin öncelikli olacağına karar verdiğini belirtir. İkisini birlikte desteklerseniz hangisinin kazandığını belgelemeyi unutmayın.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Rust öğrenme rehberi · Rust nedir? · Dosya okuma ve yazma · Result ve hata yönetimi · Iterator kullanımı · Linux nedir?