Command Line Arguments (Komut Satırı Argümanları)
Şimdiye kadar yazdığın programlar hep aynı şekilde çalıştı: derle, çalıştır, kullanıcıdan girdi al. Ama profesyonel araçlar böyle çalışmaz. git commit -m "mesaj" yazdığında, g++ -o program main.cpp dediğinde veya ls -la /home komutunu verdiğinde — hep programa çalıştırma anında talimatlar veriyorsun. İşte bunlar komut satırı argümanları (command line arguments).
Bunu bir restoran siparişi gibi düşün. Menü sistemi olan programlar "otur, menüyü oku, seç" diyor — interaktif. Ama komut satırı argümanları paket sipariş gibi: "Bir Adana, yanında pilav, acılı olsun" diyorsun ve işlem başlıyor. Neye ihtiyacın olduğunu zaten biliyorsun, menüye bakman gerekmiyor.
argc ve argv Nedir?
main Fonksiyonunun İki Yüzü
Şimdiye kadar main fonksiyonunu hep parametresiz yazdın:
int main() {
return 0;
}Ama main'in komut satırı argümanlarını alan bir versiyonu da var:
int main(int argc, char* argv[]) {
return 0;
}İki parametre:
`argc` (argument count): Programa verilen argüman sayısı. Her zaman en az 1'dir.
`argv` (argument vector): Argümanların string dizisi. Her eleman bir C-string'dir (
char*).
Önemli detay: `argv[0]` her zaman programın kendi adıdır. Yani hiç argüman vermesen bile argc 1 olur ve argv[0] programın yolunu (path) tutar.
#include <iostream>
int main(int argc, char* argv[]) {
std::cout << "Arguman sayisi: " << argc << std::endl;
for (int i = 0; i < argc; ++i) {
std::cout << "argv[" << i << "] = " << argv[i] << std::endl;
}
return 0;
}Bu programı derleyip farklı şekillerde çalıştıralım:
$ ./program
Arguman sayisi: 1
argv[0] = ./program
$ ./program merhaba dunya
Arguman sayisi: 3
argv[0] = ./program
argv[1] = merhaba
argv[2] = dunya
$ ./program --verbose -n 5 dosya.txt
Arguman sayisi: 5
argv[0] = ./program
argv[1] = --verbose
argv[2] = -n
argv[3] = 5
argv[4] = dosya.txtDikkat et: her şey boşlukla ayrılmış string. 5 sayısı değil, "5" string'idir. Sayıya çevirmek senin işin.
💡 İpucu:
argv'nin alternatif bir yazım biçimi de var:char** argv. İkisi aynı şey —char*dizisine pointer. Hangisini kullanırsan kullan, davranış aynı.
Basit Kullanım Örnekleri
Argümanlara Erişim ve String Karşılaştırma
C-string'lerle çalışırken iki yolun var: strcmp ile C tarzı karşılaştırma veya std::string'e dönüştürüp == operatörü kullanma. İkinci yol çok daha temiz:
#include <iostream>
#include <string>
int main(int argc, char* argv[]) {
// C-string → std::string dönüşümü ile çalışmak daha kolay
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i]; // otomatik dönüşüm
if (arg == "--help" || arg == "-h") {
std::cout << "Kullanim: program [secenekler]" << std::endl;
std::cout << " --help, -h Bu mesaji goster" << std::endl;
std::cout << " --version Versiyon bilgisi" << std::endl;
return 0;
}
if (arg == "--version") {
std::cout << "Program v1.0.0" << std::endl;
return 0;
}
std::cout << "Bilinmeyen arguman: " << arg << std::endl;
}
if (argc == 1) {
std::cout << "Arguman verilmedi. --help ile yardim alin." << std::endl;
}
return 0;
}argv[i]'yi std::string'e atamak sadece bir satır ve hayatını çok kolaylaştırır. == ile karşılaştırma, strcmp ile uğraşmaktan kat kat daha okunabilir.
Döngünün i = 1'den başladığına dikkat et — çünkü argv[0] programın adı, genellikle ilgilenmeyiz.
Selamlama Programı
Basit ama öğretici bir örnek:
#include <iostream>
#include <string>
int main(int argc, char* argv[]) {
if (argc < 2) {
std::cout << "Kullanim: " << argv[0] << " <isim> [yas]" << std::endl;
return 1;
}
std::string isim = argv[1];
std::cout << "Merhaba, " << isim << "!" << std::endl;
if (argc >= 3) {
std::string yasStr = argv[2];
std::cout << "Yasin: " << yasStr << std::endl;
}
return 0;
}$ ./selam
Kullanim: ./selam <isim> [yas]
$ ./selam Ahmet
Merhaba, Ahmet!
$ ./selam Ahmet 25
Merhaba, Ahmet!
Yasin: 25Burada iyi bir pratik var: argüman sayısı yetersizse kullanım mesajı (usage message) göster ve sıfır olmayan çıkış kodu ile çık. Profesyonel CLI araçlarının hepsi bunu yapar.
String → Sayı Dönüşümü
stoi, stod, stol
Argümanlar her zaman string gelir. Sayısal değerlere ihtiyacın varsa dönüşüm yapman gerekir. C++11 ile gelen stoi (string to int), stod (string to double) ve stol (string to long) fonksiyonları bu iş için biçilmiş kaftan.
Ama dikkat: geçersiz bir string verirsen bu fonksiyonlar exception fırlatır. Komut satırı argümanlarında kullanıcının ne vereceğini asla bilemezsin — bu yüzden try-catch şart.
#include <iostream>
#include <string>
#include <stdexcept>
int main(int argc, char* argv[]) {
if (argc != 3) {
std::cout << "Kullanim: " << argv[0]
<< " <sayi1> <sayi2>" << std::endl;
return 1;
}
try {
int a = std::stoi(argv[1]);
int b = std::stoi(argv[2]);
std::cout << a << " + " << b << " = " << (a + b) << std::endl;
std::cout << a << " * " << b << " = " << (a * b) << std::endl;
} catch (const std::invalid_argument& e) {
std::cerr << "Hata: Gecersiz sayi formati!" << std::endl;
std::cerr << "Detay: " << e.what() << std::endl;
return 1;
} catch (const std::out_of_range& e) {
std::cerr << "Hata: Sayi cok buyuk veya cok kucuk!" << std::endl;
return 1;
}
return 0;
}$ ./hesap 10 20
10 + 20 = 30
10 * 20 = 200
$ ./hesap abc 20
Hata: Gecersiz sayi formati!
$ ./hesap 99999999999999999999 1
Hata: Sayi cok buyuk veya cok kucuk!⚠️ Dikkat:
stoi("42abc")çağrısı hata vermez —42döner ve gerisini görmezden gelir. Eğer tamamen sayısal olmasını istiyorsan,stoi'nin ikinci parametresini (pos) kullanarak tüm string'in tüketilip tüketilmediğini kontrol edebilirsin.
std::string girdi = "42abc";
std::size_t pos;
int sayi = std::stoi(girdi, &pos);
if (pos != girdi.length()) {
std::cout << "Uyari: string tamamen parse edilmedi!" << std::endl;
std::cout << "Parse edilen: " << sayi << ", kalan: " << girdi.substr(pos) << std::endl;
}Flag Pattern'leri
Uzun ve Kısa Flag'ler
CLI araçlarında iki tür flag vardır:
Kısa flag'ler: Tek tire ve tek harf:
-v,-n,-oUzun flag'ler: Çift tire ve tam kelime:
--verbose,--count,--output
Bazı flag'ler tek başına anlam taşır (boolean flag: --verbose), bazıları yanlarında bir değer ister (--output dosya.txt, -n 5).
#include <iostream>
#include <string>
int main(int argc, char* argv[]) {
// Varsayılan değerler
bool verbose = false;
int tekrar = 1;
std::string mesaj = "Merhaba Dunya";
// Argümanları parse et
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
if (arg == "--verbose" || arg == "-v") {
verbose = true;
}
else if (arg == "--repeat" || arg == "-n") {
// Sonraki argüman değer olmalı
if (i + 1 < argc) {
++i; // bir sonraki argümana atla
try {
tekrar = std::stoi(argv[i]);
} catch (...) {
std::cerr << "Hata: --repeat icin gecerli sayi gerekli" << std::endl;
return 1;
}
} else {
std::cerr << "Hata: --repeat icin deger gerekli" << std::endl;
return 1;
}
}
else if (arg == "--message" || arg == "-m") {
if (i + 1 < argc) {
++i;
mesaj = argv[i];
} else {
std::cerr << "Hata: --message icin deger gerekli" << std::endl;
return 1;
}
}
else {
std::cerr << "Bilinmeyen arguman: " << arg << std::endl;
return 1;
}
}
// Argümanları kullan
if (verbose) {
std::cout << "[DEBUG] Tekrar: " << tekrar << std::endl;
std::cout << "[DEBUG] Mesaj: " << mesaj << std::endl;
}
for (int i = 0; i < tekrar; ++i) {
std::cout << mesaj << std::endl;
}
return 0;
}$ ./program
Merhaba Dunya
$ ./program --verbose --repeat 3 --message "Selam!"
[DEBUG] Tekrar: 3
[DEBUG] Mesaj: Selam!
Selam!
Selam!
Selam!
$ ./program -v -n 2 -m "Test"
[DEBUG] Tekrar: 2
[DEBUG] Mesaj: Test
Test
TestBu pattern'de ++i kullanımına dikkat et: bir flag değer bekliyorsa, döngü indeksini bir artırarak sonraki argümanı değer olarak alıyoruz. Bu klasik bir CLI parsing tekniği.
--key=value Pattern'i
Bazı araçlar --output=dosya.txt formatını destekler. Bu formatı parse etmek için string'de = işaretini araman yeterli:
#include <iostream>
#include <string>
// "--key=value" formatını parse et
// Başarılıysa true döner, key ve value'yu doldurur
bool parseKeyValue(const std::string& arg,
std::string& key, std::string& value) {
std::size_t pos = arg.find('=');
if (pos == std::string::npos) {
return false; // '=' bulunamadı
}
key = arg.substr(0, pos); // '=' öncesi
value = arg.substr(pos + 1); // '=' sonrası
return true;
}
int main(int argc, char* argv[]) {
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
std::string key, value;
if (parseKeyValue(arg, key, value)) {
std::cout << "Key: '" << key << "', Value: '" << value << "'" << std::endl;
} else {
std::cout << "Flag: '" << arg << "'" << std::endl;
}
}
return 0;
}$ ./program --output=result.txt --verbose --count=5 --name=Ahmet
Key: '--output', Value: 'result.txt'
Flag: '--verbose'
Key: '--count', Value: '5'
Key: '--name', Value: 'Ahmet'Bu iki pattern'i (space-separated ve = separated) birlikte desteklemek istersen, parser'ını buna göre tasarla. Ama küçük projeler için genellikle bir tanesini seçmek yeterli.
Pratik Örnek: Mini Dosya Aracı
Tüm kavramları birleştiren gerçekçi bir örnek. Bu program dosyaları okur, basit bir işlem uygular ve sonucu yazar:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
struct Ayarlar {
std::string girdiDosya;
std::string ciktiDosya;
bool verbose = false;
bool buyukHarf = false;
bool satirNumarasi = false;
};
void yardimGoster(const char* programAdi) {
std::cout << "Kullanim: " << programAdi << " [secenekler]" << std::endl;
std::cout << std::endl;
std::cout << "Secenekler:" << std::endl;
std::cout << " --input <dosya> Girdi dosyasi (zorunlu)" << std::endl;
std::cout << " --output <dosya> Cikti dosyasi (zorunlu)" << std::endl;
std::cout << " --upper Buyuk harfe cevir" << std::endl;
std::cout << " --number Satir numarasi ekle" << std::endl;
std::cout << " --verbose, -v Detayli bilgi goster" << std::endl;
std::cout << " --help, -h Bu mesaji goster" << std::endl;
}
bool argumanlariParsele(int argc, char* argv[], Ayarlar& ayar) {
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
if (arg == "--help" || arg == "-h") {
yardimGoster(argv[0]);
exit(0);
}
else if (arg == "--verbose" || arg == "-v") {
ayar.verbose = true;
}
else if (arg == "--upper") {
ayar.buyukHarf = true;
}
else if (arg == "--number") {
ayar.satirNumarasi = true;
}
else if (arg == "--input") {
if (i + 1 < argc) {
ayar.girdiDosya = argv[++i];
} else {
std::cerr << "Hata: --input icin dosya adi gerekli" << std::endl;
return false;
}
}
else if (arg == "--output") {
if (i + 1 < argc) {
ayar.ciktiDosya = argv[++i];
} else {
std::cerr << "Hata: --output icin dosya adi gerekli" << std::endl;
return false;
}
}
else {
std::cerr << "Bilinmeyen arguman: " << arg << std::endl;
return false;
}
}
// Zorunlu argümanları kontrol et
if (ayar.girdiDosya.empty()) {
std::cerr << "Hata: --input gerekli" << std::endl;
return false;
}
if (ayar.ciktiDosya.empty()) {
std::cerr << "Hata: --output gerekli" << std::endl;
return false;
}
return true;
}
int main(int argc, char* argv[]) {
if (argc < 2) {
yardimGoster(argv[0]);
return 1;
}
// Argümanları parse et
Ayarlar ayar;
if (!argumanlariParsele(argc, argv, ayar)) {
return 1;
}
if (ayar.verbose) {
std::cout << "[INFO] Girdi: " << ayar.girdiDosya << std::endl;
std::cout << "[INFO] Cikti: " << ayar.ciktiDosya << std::endl;
std::cout << "[INFO] Buyuk harf: " << (ayar.buyukHarf ? "evet" : "hayir") << std::endl;
std::cout << "[INFO] Satir no: " << (ayar.satirNumarasi ? "evet" : "hayir") << std::endl;
}
// Girdi dosyasını oku
std::ifstream girdi(ayar.girdiDosya);
if (!girdi.is_open()) {
std::cerr << "Hata: '" << ayar.girdiDosya << "' acilamadi" << std::endl;
return 1;
}
// Satırları oku ve işle
std::vector<std::string> satirlar;
std::string satir;
while (std::getline(girdi, satir)) {
satirlar.push_back(satir);
}
girdi.close();
if (ayar.verbose) {
std::cout << "[INFO] " << satirlar.size() << " satir okundu" << std::endl;
}
// Çıktı dosyasına yaz
std::ofstream cikti(ayar.ciktiDosya);
if (!cikti.is_open()) {
std::cerr << "Hata: '" << ayar.ciktiDosya << "' olusturulamadi" << std::endl;
return 1;
}
for (std::size_t i = 0; i < satirlar.size(); ++i) {
std::string islenmisSatir = satirlar[i];
// Büyük harfe çevir
if (ayar.buyukHarf) {
std::transform(islenmisSatir.begin(), islenmisSatir.end(),
islenmisSatir.begin(), ::toupper);
}
// Satır numarası ekle
if (ayar.satirNumarasi) {
cikti << (i + 1) << ": ";
}
cikti << islenmisSatir << std::endl;
}
cikti.close();
if (ayar.verbose) {
std::cout << "[INFO] " << satirlar.size() << " satir yazildi" << std::endl;
}
std::cout << "Islem tamamlandi: " << ayar.ciktiDosya << std::endl;
return 0;
}Kullanım:
# Derleme
$ g++ -o tool main.cpp -std=c++17
# Basit kullanım
$ ./tool --input veri.txt --output sonuc.txt
# Tüm seçeneklerle
$ ./tool --input veri.txt --output sonuc.txt --upper --number --verbose
[INFO] Girdi: veri.txt
[INFO] Cikti: sonuc.txt
[INFO] Buyuk harf: evet
[INFO] Satir no: evet
[INFO] 5 satir okundu
[INFO] 5 satir yazildi
Islem tamamlandi: sonuc.txt
# Yardım
$ ./tool --helpBu örnekte öğrendiğin her şey var:
argc/argvile argüman almastd::stringdönüşümü ve karşılaştırmaFlag parsing (boolean flag'ler ve değer alan flag'ler)
Eksik veya hatalı argüman durumlarında anlamlı hata mesajları
stoikullanılabilecek yerlerde sayısal dönüşüm (bu örnekte gerek olmadı ama pattern aynı)Struct ile ayarları gruplandırma — bu çok önemli bir tasarım kararı
Neden Struct Kullandık?
Ayarları bir struct'ta toplamak yerine ayrı değişkenler de kullanabilirdik. Ama struct yaklaşımının büyük avantajları var:
Fonksiyonlara tek parametre olarak geçirebilirsin
Yeni bir seçenek eklediğinde sadece struct'a bir alan eklersin
Varsayılan değerleri struct tanımında belirleyebilirsin
Kod çok daha okunabilir olur
İleri Okuma: CLI Kütüphaneleri
Bu derste elle argüman parsing yaptık — if-else zincirleriyle. Küçük projeler için bu yeterli. Ama argüman sayısı arttıkça bu yaklaşım sürdürülemez hale gelir:
Her yeni flag için if-else eklemen gerekir
Otomatik
--helpüretimi yokAlt komutlar (subcommands) desteklenmez
Tip dönüşümü ve validasyon elle yapılır
İşte bu yüzden CLI parsing kütüphaneleri var:
CLI11 — Modern C++11 kütüphanesi. Header-only (tek dosya). Çok popüler ve kullanımı kolay. Ufak-orta projeler için ideal.
Boost.Program_options — Boost ekosisteminin parçası. Çok güçlü ama Boost bağımlılığı gerektirir. Büyük projeler için.
argparse — Python'un argparse modülünden esinlenmiş. Header-only C++17 kütüphanesi. Basit ve tanıdık bir arayüz sunar.
Bu kütüphanelerin detayları bu dersin kapsamı dışında. Ama varlıklarını bil — 10'dan fazla argümanı olan bir program yazdığında, elle parsing yerine bir kütüphane kullanman çok daha mantıklı olacak.
💡 İpucu: Komut satırı argümanları sadece terminal programları için değil. GUI programları bile genellikle komut satırı argümanlarını destekler:
--config dosya.json,--fullscreen,--debuggibi. Bir dosyaya çift tıkladığında, işletim sistemi o dosyanın yolunu argüman olarak ilgili programa verir.
Özet
`argc` program başlatıldığında verilen argüman sayısını, `argv` ise bu argümanların string dizisini tutar.
argv[0]her zaman programın adıdır.Argümanlar her zaman string olarak gelir. Sayısal değerler için
stoi,stodgibi fonksiyonlarla dönüşüm yapman gerekir — ve bu dönüşümleritry-catchile sarmayı unutma.Flag pattern'leri iki türdür: boolean flag'ler (
--verbose) ve değer alan flag'ler (--output dosya.txt). Değer alan flag'lerde sonraki argümanı okumayı ve argüman bitip bitmediğini kontrol etmeyi unutma.`--key=value` formatı
string::find('=')vesubstrile kolayca parse edilir.Ayarları bir struct'ta toplamak, kodu organize etmenin ve fonksiyonlara geçmenin en temiz yoludur.
Büyük projelerde elle argüman parsing yerine CLI11 veya Boost.Program_options gibi kütüphaneler kullanılır. 10+ argümandan sonra bu kütüphaneler ciddi zaman kazandırır.
AI Asistan
Sorularını yanıtlamaya hazır