Yazar: @commandwarrior
Tarih: 28 Nisan 2026
Sürüm: v3.3 (Bash) / v3.3 (PowerShell)
Platform: Linux Mint 22.2 MATE / Windows 11 Home 24H2
1. Giriş
Mobil internet paketlerinin sınırlı kota ve belirli geçerlilik süreleri barındırması, kullanıcıların günlük tüketimlerini bilinçli yönetmesini zorunlu kılmaktadır. Bu çalışmada, kullanıcıdan alınan paket bilgileri doğrultusunda kalan kota, geçen/kalan gün sayısı ve önerilen günlük kullanım miktarını hesaplayan bir komut satırı aracının geliştirilme süreci ele alınmaktadır. Araç, önce Bash (net-usage-optimizer.sh) olarak tasarlanmış; ardından çapraz platform desteği amacıyla PowerShell (net-usage-optimizer.ps1) diline taşınmıştır. Her iki sürümde de karşılaşılan hatalar sistematik biçimde tespit edilip giderilmiş, betiklerin sağlamlığı ve kullanılabilirliği artırılmıştır.
2. Gelişme
2.1. Betiğin Temel İşlev Yapısı
Araç dört ana aşamadan oluşmaktadır:

2.2.1. Sürüm Uyuşmazlığı
Betiğin başlık satırındaki sürüm etiketi (v2.0), dosya adındaki sürümle (v3_0) çelişmekteydi. Başlık v3.0 olarak düzeltilmiştir.
2.2.2. read Komutunda -r Bayrağının Eksikliği
Bash'te -r bayrağı kullanılmadan çağrılan read -p komutu, girişte yer alan ters eğik çizgi (\) karakterlerini kaçış dizisi olarak yorumlar. Bu durum, özellikle dosya yolu veya tarih girdilerinde beklenmedik davranışlara yol açabilmektedir. Tüm read çağrıları read -rp biçimine dönüştürülmüştür.
2.2.3. Birim Sonekinin Sayısal Hesabı Bozması
Kullanıcıların 50 GB veya 11.78 GB biçiminde girdi vermesi durumunda, fix_decimal() işlevi yalnızca virgülü noktaya dönüştürmekte; GB metnini ayrıştırmamaktaydı. Bu durum bc aracına "50 GB - 11.78 GB" dizesinin iletilmesine ve syntax error üretilmesine neden olmaktaydı. grep -oP '^[0-9]*\.?[0-9]+' eklenerek sayının ötesindeki tüm karakterler temizlenmiştir.
2.2.4. LC_ALL ile LC_NUMERIC Çakışması
export LC_ALL=tr_TR.UTF-8 ayarı, Bash'te diğer tüm LC_* değişkenlerini ezmektedir. Bu nedenle sonradan tanımlanan export LC_NUMERIC=C etkisiz kalmakta; printf "%.2f" komutu ondalık ayırıcı olarak nokta değil virgül beklemekte ve printf: 12.9781: geçersiz sayı hatası üretmekteydi. Çözüm olarak LC_ALL kaldırılmış; LC_CTYPE, LC_MESSAGES ve LC_TIME Türkçe'ye, LC_NUMERIC ise C yerel ayarına ayrı ayrı atanmıştır:
Kod: Tümünü seç
export LC_CTYPE=tr_TR.UTF-8
export LC_MESSAGES=tr_TR.UTF-8
export LC_TIME=tr_TR.UTF-8
export LC_NUMERIC=Cbc aracı, 0 ile 1 arasındaki ondalık sonuçları baştaki sıfır olmadan üretmektedir (örn. .90 yerine 0.90 beklenir). Bu durum printf "%.2f" ile de uyumsuzluk yaratmaktaydı. fix_leading_zero() işlevi eklenerek tüm bc çıktıları bu işlevden geçirilmiştir:
Kod: Tümünü seç
fix_leading_zero() {
local val="$1"
[[ "$val" == .* ]] && val="0${val}"
[[ "$val" == -.* ]] && val="-0${val:1}"
echo "$val"
}İlk tasarımda yalnızca YYYY-MM-DD HH:MM:SS biçimi kabul edilmekteydi. Kullanıcının 15 Nisan 2026 , 15:02 biçiminde giriş yapması reddedilmekteydi. normalize_date() işlevi geliştirilmiş; bu işlev virgülleri temizlemekte, ardışık boşlukları tekile indirgemekte ve Türkçe ay adlarını sed ile İngilizce karşılıklarına dönüştürmektedir. date -d komutu ardından normalleştirilmiş dizeyi ayrıştırmakta ve kanonik biçimi döndürmektedir.
2.2.7. Negatif Kalan Gün Görüntüsü
Paket süresi dolmuşsa REMAINING_DAYS değişkeni negatif hesaplanmakta ve ekrana Kalan Gün: -X.XX gün basılmaktaydı. Kontrol bloğu güncellenerek REMAINING_DAYS <= 0 durumunda "Paket süresi dolmuş" mesajı gösterilmesi sağlanmıştır.
2.2.8. Kota Aşımı ve Süre Dolumu Durumlarının TAVSİYE Bloğunda Ele Alınmaması
Özgün TAVSİYE bloğu yalnızca normal kullanım senaryolarını kapsamaktaydı. Kota aşımı (REMAINING_GB <= 0) ve paket süresi dolumu için iki yeni öncelikli dal eklenmiştir.
2.3. PowerShell Portuna Özgü Tasarım Kararları
Bash betiğinin Windows PowerShell'e taşınmasında her iki ortam arasındaki yapısal farklılıklar gözetilmiştir:


net-usage-optimizer betiği, başlangıçta basit bir kota hesaplama aracı olarak tasarlanmış; ancak geliştirme sürecinde Bash locale yönetimi, bc aritmetik çıktısı, kullanıcı girdi esnekliği ve çapraz platform uyumluluğu gibi birden fazla teknik katmanı kapsayan sorunlarla yüzleşilmiştir. Her hata, betiğin yalnızca işlevsel değil aynı zamanda sağlam ve kullanılabilir olması gerektiğini ortaya koymuştur. Özellikle LC_ALL ile LC_NUMERIC arasındaki öncelik çakışması, Bash yerel ayar mimarisinin gözden kaçması kolay bir inceliğini gün yüzüne çıkarmıştır. PowerShell portunda ise Türkçe Unicode karakterlerin regex motoruyla olan uyumsuzluğu, normalleştirme katmanı eklenmesini zorunlu kılmıştır. Sonuç olarak her iki platform için tutarlı, hatasız ve genişletilebilir bir araç ortaya konulmuştur.
KAYNAK KOD - 1 ( Bash Shell-GNU/Linux------ net-usage-optimizer-v3.3.sh ) :
Kod: Tümünü seç
#!/bin/bash
# -------- UTF-8 GUARANTEE --------
# LC_ALL is intentionally NOT set: it overrides every other LC_* variable,
# making LC_NUMERIC=C ineffective. Individual categories are set instead.
export LANG=tr_TR.UTF-8
export LC_CTYPE=tr_TR.UTF-8 # Turkish character display
export LC_MESSAGES=tr_TR.UTF-8 # Turkish system messages
export LC_TIME=tr_TR.UTF-8 # Turkish date/time strings
export LC_NUMERIC=C # dot as decimal separator for bc/printf
clear
echo "========================================"
echo "İNTERNET KULLANIM ANALİZ ARACI (v3.0)"
echo "========================================"
# -------- DEPENDENCY CHECK --------
if ! command -v bc &> /dev/null; then
echo "HATA: 'bc' aracı yüklü değil!"
echo "Kurmak için: sudo apt install bc"
read -rp $'\nPress ENTER to exit.' _
exit 1
fi
# -------- FUNCTION: DECIMAL FIX --------
# Converts comma decimal separator to dot AND strips any unit suffix (GB, MB, kb ...)
fix_decimal() {
echo "$1" | tr ',' '.' | grep -oP '^[0-9]*\.?[0-9]+'
}
# -------- FUNCTION: LEADING ZERO FIX --------
# bc omits the leading zero for values like .90 — this restores it to 0.90
fix_leading_zero() {
local val="$1"
# If value starts with a dot (e.g. .90), prepend zero
if [[ "$val" == .* ]]; then
val="0${val}"
# If value starts with -. (e.g. -.90), fix to -0.90
elif [[ "$val" == -.* ]]; then
val="-0${val:1}"
fi
echo "$val"
}
# -------- FUNCTION: DATE NORMALIZATION --------
# Accepts flexible Turkish/English date strings, returns YYYY-MM-DD HH:MM:SS
# Handles: Turkish month names, comma separators, missing seconds, etc.
normalize_date() {
local raw="$1"
# Remove commas and collapse multiple spaces
local cleaned
cleaned=$(echo "$raw" | tr ',' ' ' | tr -s ' ')
# Map Turkish month names to English (case-insensitive)
cleaned=$(echo "$cleaned" | \
sed 's/[Oo]cak/January/g' | \
sed 's/[Şş]ubat/February/g' | \
sed 's/[Mm]art/March/g' | \
sed 's/[Nn]isan/April/g' | \
sed 's/[Mm]ayı[sS]/May/g' | \
sed 's/[Hh]aziran/June/g' | \
sed 's/[Tt]emmuz/July/g' | \
sed 's/[Aa]ğustos/August/g' | \
sed 's/[Ee]ylül/September/g'| \
sed 's/[Ee]kim/October/g' | \
sed 's/[Kk]asım/November/g' | \
sed 's/[Aa]ralı[kK]/December/g')
# Try to parse; if successful output canonical form, else return error
date -d "$cleaned" +"%Y-%m-%d %H:%M:%S" 2>/dev/null
}
# -------- USER INPUT --------
read -rp "Internet Paket Adı: " PACKAGE_NAME
read -rp "Toplam GB Limiti: " TOTAL_GB_RAW
TOTAL_GB=$(fix_decimal "$TOTAL_GB_RAW")
read -rp "Kullanılan GB (örn: 11,78 veya 11.78): " USED_GB_RAW
USED_GB=$(fix_decimal "$USED_GB_RAW")
echo ""
echo "Kabul edilen tarih örnekleri:"
echo " 2026-04-15 15:02:00"
echo " 15 Nisan 2026 15:02"
echo " 15 April 2026 , 15:02"
echo " April 15 2026 15:02:00"
while true; do
read -rp "Paket Başlangıç Tarihi: " START_DATE_RAW
START_DATE=$(normalize_date "$START_DATE_RAW")
if [[ -n "$START_DATE" ]]; then
echo " → $START_DATE"
break
else
echo "Tanınamayan tarih! Lütfen yukarıdaki örneklerden birini deneyin."
fi
done
while true; do
read -rp "Paket Bitiş Tarihi: " END_DATE_RAW
END_DATE=$(normalize_date "$END_DATE_RAW")
if [[ -n "$END_DATE" ]]; then
echo " → $END_DATE"
break
else
echo "Tanınamayan tarih! Lütfen yukarıdaki örneklerden birini deneyin."
fi
done
echo ""
read -rp "Veri Kaynağı (örn: Android Data Manager): " DATA_SOURCE
# -------- CURRENT TIME --------
NOW=$(date +"%Y-%m-%d %H:%M:%S")
# -------- TIME CONVERSION --------
START_SEC=$(date -d "$START_DATE" +%s)
END_SEC=$(date -d "$END_DATE" +%s)
NOW_SEC=$(date -d "$NOW" +%s)
# -------- SAFETY CHECK --------
if (( END_SEC <= START_SEC )); then
echo "HATA: Bitiş tarihi başlangıçtan büyük olmalı!"
read -rp $'\nPress ENTER to exit.' _
exit 1
fi
# -------- CALCULATIONS --------
REMAINING_GB=$(fix_leading_zero "$(echo "$TOTAL_GB - $USED_GB" | bc)")
ELAPSED_SEC=$((NOW_SEC - START_SEC))
REMAINING_SEC=$((END_SEC - NOW_SEC))
ELAPSED_DAYS=$(echo "scale=4; $ELAPSED_SEC / 86400" | bc)
REMAINING_DAYS=$(echo "scale=4; $REMAINING_SEC / 86400" | bc)
# Division safety
if (( $(echo "$ELAPSED_DAYS <= 0" | bc -l) )); then
DAILY_AVG=0
else
DAILY_AVG=$(fix_leading_zero "$(echo "scale=2; $USED_GB / $ELAPSED_DAYS" | bc)")
fi
if (( $(echo "$REMAINING_DAYS <= 0" | bc -l) )); then
DAILY_LIMIT=0
SAFE_LIMIT=0
else
DAILY_LIMIT=$(fix_leading_zero "$(echo "scale=2; $REMAINING_GB / $REMAINING_DAYS" | bc)")
SAFE_LIMIT=$(fix_leading_zero "$(echo "scale=2; $DAILY_LIMIT * 0.9" | bc)")
fi
# -------- OUTPUT --------
echo ""
echo "========================================"
echo "ANALİZ SONUCU"
echo "========================================"
echo "Paket Adı : $PACKAGE_NAME"
echo "Veri Kaynağı : $DATA_SOURCE"
echo "----------------------------------------"
echo "Toplam : $TOTAL_GB GB"
echo "Kullanılan : $USED_GB GB"
echo "Kalan : $REMAINING_GB GB"
echo "----------------------------------------"
printf "Geçen Gün : %.2f gün\n" "$ELAPSED_DAYS"
# BUG FIX 4: show "expired" message instead of negative remaining days
if (( $(echo "$REMAINING_DAYS <= 0" | bc -l) )); then
echo "Kalan Gün : Paket süresi dolmuş"
else
printf "Kalan Gün : %.2f gün\n" "$REMAINING_DAYS"
fi
echo "----------------------------------------"
echo "Ortalama Kullanım : $DAILY_AVG GB/gün"
echo "Günlük Limit : $DAILY_LIMIT GB/gün"
echo "Güvenli Limit : $SAFE_LIMIT GB/gün"
echo "========================================"
# -------- SMART COMMENT --------
echo ""
echo "TAVSİYE:"
# BUG FIX 5: handle quota exceeded case first
if (( $(echo "$REMAINING_GB <= 0" | bc -l) )); then
echo "- Paket kotası aşılmış, kullanımı durdur!"
elif (( $(echo "$REMAINING_DAYS <= 0" | bc -l) )); then
echo "- Paket süresi dolmuş, yenileme zamanı."
elif (( $(echo "$DAILY_AVG < $SAFE_LIMIT" | bc -l) )); then
echo "- Kullanımın düşük, rahatça artırabilirsin."
elif (( $(echo "$DAILY_AVG > $DAILY_LIMIT" | bc -l) )); then
echo "- Fazla hızlı tüketiyorsun, dikkat et!"
else
echo "- Dengeli kullanım, bu şekilde devam."
fi
echo ""
read -rp $'\nPress ENTER to exit.' _
Kod: Tümünü seç
# ======================================================
# NET-USAGE-OPTIMIZER v3.3 | PowerShell Edition
# Windows 11 / PowerShell 5.1+
# ======================================================
# -------- CONSOLE ENCODING (UTF-8 for Turkish chars) --------
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$OutputEncoding = [System.Text.Encoding]::UTF8
Clear-Host
Write-Host "========================================"
Write-Host "INTERNET KULLANIM ANALIZ ARACI (v3.3)"
Write-Host "========================================"
# -------- FUNCTION: NUMERIC INPUT FIX --------
# Strips unit suffixes (GB, MB ...) and converts comma decimal to dot,
# then returns a [double].
function Fix-Numeric {
param([string]$Raw)
# Keep only digits, dot, comma, minus
$cleaned = $Raw -replace '[^0-9.,\-]', ''
# Replace comma decimal separator with dot
$cleaned = $cleaned -replace ',', '.'
if ($cleaned -eq '') { return $null }
try { return [double]$cleaned } catch { return $null }
}
# -------- FUNCTION: DATE NORMALIZATION --------
# Accepts Turkish/English free-form dates; returns a [datetime] or $null.
function Normalize-Date {
param([string]$Raw)
# Remove commas, collapse whitespace
$s = $Raw -replace ',', ' '
$s = $s -replace '\s+', ' '
$s = $s.Trim()
# Turkish month names -> English
$months = @{
'Ocak' = 'January'; 'ocak' = 'January'
'Subat' = 'February'; 'subat' = 'February'
'Mart' = 'March'; 'mart' = 'March'
'Nisan' = 'April'; 'nisan' = 'April'
'Mayis' = 'May'; 'mayis' = 'May'
'Haziran' = 'June'; 'haziran' = 'June'
'Temmuz' = 'July'; 'temmuz' = 'July'
'Agustos' = 'August'; 'agustos' = 'August'
'Eylul' = 'September';'eylul' = 'September'
'Ekim' = 'October'; 'ekim' = 'October'
'Kasim' = 'November'; 'kasim' = 'November'
'Aralik' = 'December'; 'aralik' = 'December'
}
# Normalize Turkish-specific characters for matching, keep original for replace
$normalized = $s `
-replace '[Ss]ubat', 'Subat' `
-replace '[Mm]ay.s', 'Mayis' `
-replace '[Aa]gustos', 'Agustos'`
-replace '[Ee]yl.l', 'Eylul' `
-replace '[Kk]as.m', 'Kasim' `
-replace '[Aa]ral.k', 'Aralik'
foreach ($tr in $months.Keys) {
$normalized = $normalized -replace "(?i)\b$tr\b", $months[$tr]
}
# Try parsing with various cultures
$cultures = @(
[System.Globalization.CultureInfo]::InvariantCulture,
[System.Globalization.CultureInfo]::new('en-US'),
[System.Globalization.CultureInfo]::new('tr-TR')
)
foreach ($culture in $cultures) {
$dt = [datetime]::MinValue
if ([datetime]::TryParse($normalized, $culture,
[System.Globalization.DateTimeStyles]::None, [ref]$dt)) {
return $dt
}
}
return $null
}
# -------- USER INPUT --------
$packageName = Read-Host "Internet Paket Adi"
do {
$totalGbRaw = Read-Host "Toplam GB Limiti"
$totalGb = Fix-Numeric $totalGbRaw
if ($null -eq $totalGb) { Write-Host "Gecersiz sayi, tekrar deneyin." }
} while ($null -eq $totalGb)
do {
$usedGbRaw = Read-Host "Kullanilan GB (orn: 11,78 veya 11.78)"
$usedGb = Fix-Numeric $usedGbRaw
if ($null -eq $usedGb) { Write-Host "Gecersiz sayi, tekrar deneyin." }
} while ($null -eq $usedGb)
Write-Host ""
Write-Host "Kabul edilen tarih ornekleri:"
Write-Host " 2026-04-15 15:02:00"
Write-Host " 15 Nisan 2026 15:02"
Write-Host " 15 April 2026 , 15:02"
Write-Host " April 15 2026 15:02:00"
do {
$startRaw = Read-Host "Paket Baslangic Tarihi"
$startDate = Normalize-Date $startRaw
if ($null -eq $startDate) {
Write-Host "Taninamayan tarih! Lutfen orneklerden birini deneyin."
} else {
Write-Host (" -> " + $startDate.ToString("yyyy-MM-dd HH:mm:ss"))
}
} while ($null -eq $startDate)
do {
$endRaw = Read-Host "Paket Bitis Tarihi"
$endDate = Normalize-Date $endRaw
if ($null -eq $endDate) {
Write-Host "Taninamayan tarih! Lutfen orneklerden birini deneyin."
} else {
Write-Host (" -> " + $endDate.ToString("yyyy-MM-dd HH:mm:ss"))
}
} while ($null -eq $endDate)
Write-Host ""
$dataSource = Read-Host "Veri Kaynagi (orn: Android Data Manager)"
# -------- SAFETY CHECK --------
if ($endDate -le $startDate) {
Write-Host "HATA: Bitis tarihi baslangictan buyuk olmali!"
Read-Host "`nPress ENTER to exit."
exit 1
}
# -------- CALCULATIONS --------
$now = Get-Date
$elapsedSec = ($now - $startDate).TotalSeconds
$remainingSec = ($endDate - $now).TotalSeconds
$totalSec = ($endDate - $startDate).TotalSeconds
$remainingGb = $totalGb - $usedGb
$elapsedDays = $elapsedSec / 86400
$remainingDays = $remainingSec / 86400
if ($elapsedDays -le 0) {
$dailyAvg = 0.0
} else {
$dailyAvg = $usedGb / $elapsedDays
}
if ($remainingDays -le 0) {
$dailyLimit = 0.0
$safeLimit = 0.0
} else {
$dailyLimit = $remainingGb / $remainingDays
$safeLimit = $dailyLimit * 0.9
}
# -------- OUTPUT --------
Write-Host ""
Write-Host "========================================"
Write-Host "ANALIZ SONUCU"
Write-Host "========================================"
Write-Host ("Paket Adi : " + $packageName)
Write-Host ("Veri Kaynagi : " + $dataSource)
Write-Host "----------------------------------------"
Write-Host ("Toplam : " + ("{0:F2}" -f $totalGb) + " GB")
Write-Host ("Kullanilan : " + ("{0:F2}" -f $usedGb) + " GB")
Write-Host ("Kalan : " + ("{0:F2}" -f $remainingGb) + " GB")
Write-Host "----------------------------------------"
Write-Host ("Gecen Gun : " + ("{0:F2}" -f $elapsedDays) + " gun")
if ($remainingDays -le 0) {
Write-Host "Kalan Gun : Paket suresi dolmus"
} else {
Write-Host ("Kalan Gun : " + ("{0:F2}" -f $remainingDays) + " gun")
}
Write-Host "----------------------------------------"
Write-Host ("Ortalama Kullanim : " + ("{0:F2}" -f $dailyAvg) + " GB/gun")
Write-Host ("Gunluk Limit : " + ("{0:F2}" -f $dailyLimit) + " GB/gun")
Write-Host ("Guvenli Limit : " + ("{0:F2}" -f $safeLimit) + " GB/gun")
Write-Host "========================================"
# -------- SMART COMMENT --------
Write-Host ""
Write-Host "TAVSIYE:"
if ($remainingGb -le 0) {
Write-Host "- Paket kotasi asilmis, kullanimi durdur!"
} elseif ($remainingDays -le 0) {
Write-Host "- Paket suresi dolmus, yenileme zamani."
} elseif ($dailyAvg -lt $safeLimit) {
Write-Host "- Kullanimin dusuk, rahatca artirabilirsin."
} elseif ($dailyAvg -gt $dailyLimit) {
Write-Host "- Fazla hizli tuketiyorsun, dikkat et!"
} else {
Write-Host "- Dengeli kullanim, bu sekilde devam."
}
Write-Host ""
Read-Host "`nPress ENTER to exit."

Kod: Tümünü seç
========================================
İNTERNET KULLANIM ANALİZ ARACI (v3.0)
========================================
Internet Paket Adı: Turkcell Hepsi Bir Arada
Toplam GB Limiti: 50 GB
Kullanılan GB (örn: 11,78 veya 11.78): 11.78 GB
Kabul edilen tarih örnekleri:
2026-04-15 15:02:00
15 Nisan 2026 15:02
15 April 2026 , 15:02
April 15 2026 15:02:00
Paket Başlangıç Tarihi: 15 Nisan 2026 15:02
→ 2026-04-15 15:02:00
Paket Bitiş Tarihi: 13 Mayıs 2026 15:02
→ 2026-05-13 15:02:00
Veri Kaynağı (örn: Android Data Manager): Android Data Manager
========================================
ANALİZ SONUCU
========================================
Paket Adı : Turkcell Hepsi Bir Arada
Veri Kaynağı : Android Data Manager
----------------------------------------
Toplam : 50 GB
Kullanılan : 11.78 GB
Kalan : 38.22 GB
----------------------------------------
Geçen Gün : 12.98 gün
Kalan Gün : 15.02 gün
----------------------------------------
Ortalama Kullanım : 0.90 GB/gün
Günlük Limit : 2.54 GB/gün
Güvenli Limit : 2.28 GB/gün
========================================
TAVSİYE:
- Kullanımın düşük, rahatça artırabilirsin.
Press ENTER to exit.
Bash Shell ortamında (kabuk içinde) PowerShell ortamına geçilmesi ve betiğin çalıştırılması işlemi (Güncelleme gelmiş)









