printf ile Biçimli Çıktı Üretme

Terminal üzerinde çalışırken bir noktada echo komutunun yetersiz kaldığını fark edersiniz. Belki bir rapor çıktısı hazırlıyorsunuzdur, belki log dosyalarını belirli bir formatta üretmeniz gerekiyordur ya da sayısal verileri hizalanmış şekilde göstermek istiyorsunuzdur. İşte tam bu noktada printf devreye girer ve size programlama dillerindeki kadar güçlü bir biçimlendirme kapasitesi sunar.

printf, adını C programlama dilinin printf() fonksiyonundan alır ve aynı biçimlendirme mantığını terminal ortamına taşır. echo komutundan çok daha esnek olan bu araç, özellikle script yazarken ve metin işleme görevlerinde vazgeçilmez hale gelir.

printf Nedir ve echo’dan Farkı Nedir?

echo komutu basit metin çıktıları için yeterlidir ama sınırlıdır. Satır sonu karakterini otomatik ekler, biçimlendirme seçenekleri kısıtlıdır ve farklı sistemlerde (bash, sh, dash) davranışı tutarsız olabilir. printf ise tam tersi bir yaklaşım sunar: ne söylersen onu yapar, fazlasını değil.

Temel fark şudur: printf otomatik olarak satır sonu eklemez. Satır sonu istiyorsan n kullanman gerekir. Bu davranış ilk başta can sıkıcı gelebilir ama aslında size tam kontrol sağlar.

# echo ile basit çıktı
echo "Merhaba Dünya"

# printf ile aynı şey
printf "Merhaba Dünyan"

# printf'in echo'dan farkı: otomatik satır sonu yok
printf "Birinci "
printf "İkincin"
# Çıktı: Birinci İkinci

printf komutu hem Bash’in built-in komutu olarak hem de /usr/bin/printf şeklinde bağımsız bir program olarak mevcuttur. Çoğu durumda built-in versiyonu kullanırsınız, bu da onu son derece hızlı yapar.

Temel Sözdizimi

printf FORMAT [ARGÜMAN]...

Format dizesi, düz metin ve biçim belirteçlerinin bir kombinasyonudur. Biçim belirteçleri % işaretiyle başlar ve ardından gelen karakter türü belirler.

Temel biçim belirteçleri:

  • %s: Karakter dizisi (string)
  • %d: Tam sayı (integer)
  • %f: Ondalıklı sayı (float)
  • %e: Bilimsel gösterim
  • %o: Sekizlik (octal) sayı
  • %x: On altılık (hexadecimal) sayı, küçük harf
  • %X: On altılık sayı, büyük harf
  • %c: Tek karakter
  • %%: Yüzde işareti kendisi

Kaçış dizileri (escape sequences):

  • n: Yeni satır
  • t: Tab karakteri
  • r: Satır başı (carriage return)
  • \: Ters bölü işareti
  • a: Zil sesi (bell)
  • b: Geri al (backspace)

Temel Kullanım Örnekleri

İlk olarak en sık kullanılan basit örneklere bakalım:

# String biçimlendirme
printf "Kullanıcı adı: %sn" "ahmet"

# Tam sayı
printf "Port numarası: %dn" 8080

# Ondalıklı sayı
printf "CPU kullanımı: %f%%n" 75.5

# Çoklu argüman
printf "Ad: %s, Yaş: %dn" "Mehmet" 35

# printf format dizesini tekrar kullanır
printf "Sunucu: %sn" "web01" "web02" "web03"

Son örnekteki davranış özellikle dikkat çekicidir. Eğer argüman sayısı format dizesindeki belirteç sayısından fazlaysa, printf format dizesini baştan tekrarlar. Bu özelliği döngü yerine kullanabilirsiniz.

Genişlik ve Hizalama

printf‘in en güçlü yanlarından biri, çıktıyı belirli bir genişlikte hizalayabilmesidir. Bu özellik özellikle tablo benzeri çıktılar üretirken çok işe yarar.

# Sağa hizalama (varsayılan)
printf "%10s %10s %10sn" "Sunucu" "IP" "Port"
printf "%10s %10s %10dn" "web01" "192.168.1.10" 80
printf "%10s %10s %10dn" "db01" "192.168.1.20" 5432
printf "%10s %10s %10dn" "cache01" "192.168.1.30" 6379

# Sola hizalama (eksi işareti ile)
printf "%-15s %-15s %-6sn" "Sunucu" "IP Adresi" "Port"
printf "%-15s %-15s %-6dn" "web01" "192.168.1.10" 80
printf "%-15s %-15s %-6dn" "db01" "192.168.1.20" 5432

Bu çıktı şu şekilde görünür:

Sunucu          IP Adresi       Port  
web01           192.168.1.10    80    
db01            192.168.1.20    5432  

Genişlik belirteçlerinin detayları:

  • %10s: En az 10 karakter genişliğinde, sağa hizalı
  • %-10s: En az 10 karakter genişliğinde, sola hizalı
  • %010d: 10 karakter genişliğinde, sıfırlarla doldurulmuş
  • %.5s: Maksimum 5 karakter, fazlası kesilir
  • %10.5s: 10 karakter genişlik, 5 karakter maksimum içerik

Sayısal Biçimlendirme

Sayılarla çalışırken printf oldukça yeteneklidir:

# Ondalık hassasiyet
printf "Disk kullanımı: %.2f%%n" 67.8934
# Çıktı: Disk kullanımı: 67.89%

# Sıfırla doldurma
printf "İş numarası: %05dn" 42
# Çıktı: İş numarası: 00042

# Hexadecimal çıktı (renk kodları, MAC adresleri için kullanışlı)
printf "MAC: %02X:%02X:%02X:%02X:%02X:%02Xn" 10 20 30 40 50 60
# Çıktı: MAC: 0A:14:1E:28:32:3C

# Büyük sayıları okumak kolaylaştırma
printf "Bellek: %d bytesn" 1073741824

# Bilimsel gösterim
printf "Bilimsel: %en" 123456789.0
# Çıktı: Bilimsel: 1.234568e+08

Gerçek Dünya Senaryosu 1: Sistem İzleme Raporu

Sistem yöneticileri olarak sık sık sunucu durumunu belirli bir formatta raporlamak zorunda kalırız. İşte printf kullanarak güzel bir sistem raporu üretelim:

#!/bin/bash

# Sistem bilgilerini topla
HOSTNAME=$(hostname)
UPTIME=$(uptime -p | sed 's/up //')
CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
MEM_TOTAL=$(free -m | awk 'NR==2{print $2}')
MEM_USED=$(free -m | awk 'NR==2{print $3}')
MEM_PERCENT=$(echo "scale=2; $MEM_USED * 100 / $MEM_TOTAL" | bc)
DISK_USAGE=$(df -h / | awk 'NR==2{print $5}' | tr -d '%')
LOAD_AVG=$(cat /proc/loadavg | awk '{print $1}')

# Rapor başlığı
printf "=%.0s" {1..50}
printf "n"
printf "%-20s %sn" "SUNUCU DURUM RAPORU" "$(date '+%Y-%m-%d %H:%M:%S')"
printf "=%.0s" {1..50}
printf "nn"

# Sistem bilgileri
printf "%-20s: %sn" "Hostname" "$HOSTNAME"
printf "%-20s: %sn" "Uptime" "$UPTIME"
printf "%-20s: %.1f%%n" "CPU Kullanımı" "$CPU_USAGE"
printf "%-20s: %d MB / %d MB (%.1f%%)n" "RAM" "$MEM_USED" "$MEM_TOTAL" "$MEM_PERCENT"
printf "%-20s: %d%%n" "Disk Kullanımı" "$DISK_USAGE"
printf "%-20s: %sn" "Yük Ortalaması" "$LOAD_AVG"

printf "n"
printf "=%.0s" {1..50}
printf "n"

Bu script çalıştırıldığında düzgün hizalanmış, okunması kolay bir rapor üretir.

Gerçek Dünya Senaryosu 2: Log Dosyası Üretme

Uygulamalarınız için standart formatlı log dosyaları üretmek sysadmin işinin önemli bir parçasıdır. printf burada da mükemmel iş çıkarır:

#!/bin/bash

LOG_FILE="/var/log/uygulama/app.log"
LOG_LEVEL="INFO"

log_message() {
    local level="$1"
    local message="$2"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    local pid=$$
    
    # [2024-01-15 14:30:25] [INFO ] [PID:1234] Mesaj içeriği
    printf "[%s] [%-5s] [PID:%-6d] %sn" 
        "$timestamp" 
        "$level" 
        "$pid" 
        "$message" >> "$LOG_FILE"
}

log_info() { log_message "INFO" "$1"; }
log_warn() { log_message "WARN" "$1"; }
log_error() { log_message "ERROR" "$1"; }

# Kullanım
log_info "Uygulama başlatılıyor"
log_warn "Bellek kullanımı yüksek: 85%"
log_error "Veritabanı bağlantısı kurulamadı"

Bu yaklaşım, tüm log satırlarının aynı formatta olmasını garantiler. %-5s kullanımı sayesinde INFO, WARN, ERROR kelimeleri her zaman 5 karakter genişliğinde hizalanır, bu da grep ile filtrelemeyi kolaylaştırır.

printf ile Renkli Terminal Çıktısı

Terminal renklendirmesi için ANSI kaçış kodlarını printf ile birlikte kullanabilirsiniz. echo -e ile de yapılabilir ama printf daha güvenilirdir:

#!/bin/bash

# Renk tanımları
RED='33[0;31m'
GREEN='33[0;32m'
YELLOW='33[1;33m'
BLUE='33[0;34m'
BOLD='33[1m'
NC='33[0m' # Renk sıfırlama

# Renkli durum mesajları
print_status() {
    local status="$1"
    local message="$2"
    
    case "$status" in
        "OK")      printf "[${GREEN}%-6s${NC}] %sn" "$status" "$message" ;;
        "WARN")    printf "[${YELLOW}%-6s${NC}] %sn" "$status" "$message" ;;
        "ERROR")   printf "[${RED}%-6s${NC}] %sn" "$status" "$message" ;;
        "INFO")    printf "[${BLUE}%-6s${NC}] %sn" "$status" "$message" ;;
    esac
}

# Kullanım
print_status "OK" "Nginx servisi çalışıyor"
print_status "WARN" "Disk kullanımı %80'i geçti"
print_status "ERROR" "MySQL servisi yanıt vermiyor"
print_status "INFO" "Yedekleme başlatılıyor"

awk ile Birlikte Kullanım

printf, awk içinde de aynı sözdizimi ile kullanılır. Bu iki aracı birleştirdiğinizde metin işleme gücünüz katlanır:

# /etc/passwd dosyasından kullanıcı raporu
awk -F: '$3 >= 1000 {
    printf "%-15s UID: %-6d HOME: %sn", $1, $3, $6
}' /etc/passwd

# Disk kullanım özeti
df -h | awk 'NR>1 {
    printf "%-20s Boyut: %-6s Kullanım: %-6s Dolu: %sn", $6, $2, $3, $5
}'

# Servis durumu kontrolü
systemctl list-units --type=service --state=running | awk '/.service/ {
    printf "%-40s %sn", $1, $3
}'

awk içindeki printf, virgülle ayrılmış argümanlar alır (bash’taki gibi boşlukla değil). Bu küçük fark başlangıçta kafa karıştırabilir.

Gerçek Dünya Senaryosu 3: Crontab ve Otomatik Raporlama

Haftalık sunucu envanter raporu üretmek için printf kullanan bir script yazalım:

#!/bin/bash
# /usr/local/bin/envanter-raporu.sh

OUTPUT_FILE="/tmp/envanter_$(date +%Y%m%d).txt"
MAIL_TO="[email protected]"

{
    printf "SUNUCU ENVANTERİ - %sn" "$(date '+%d/%m/%Y %H:%M')"
    printf "%sn" "$(printf '=%.0s' {1..60})"
    printf "n"
    
    # Ağ arayüzleri
    printf "%-5s %-12s %-20s %sn" "No" "Arayüz" "IP Adresi" "Durum"
    printf "%sn" "$(printf '-%.0s' {1..60})"
    
    ip addr show | awk '
    /^[0-9]+:/ {
        iface = $2
        gsub(/:/, "", iface)
        num = $1
        gsub(/:/, "", num)
    }
    /inet [0-9]/ {
        split($2, ip, "/")
        printf "%-5s %-12s %-20s %sn", num, iface, ip[1], "aktif"
    }'
    
    printf "n"
    
    # Açık portlar
    printf "%-10s %-20s %sn" "Port" "Protokol" "Servis"
    printf "%sn" "$(printf '-%.0s' {1..60})"
    
    ss -tlnp | awk 'NR>1 {
        split($4, addr, ":")
        port = addr[length(addr)]
        printf "%-10s %-20s %sn", port, $1, $6
    }' | sort -n
    
} > "$OUTPUT_FILE"

# Mail gönder (mailutils kurulu ise)
# mail -s "Sunucu Envanteri $(date +%Y-%m-%d)" "$MAIL_TO" < "$OUTPUT_FILE"

printf "Rapor oluşturuldu: %sn" "$OUTPUT_FILE"

printf ile Dosya İçeriği Üretme

printf sadece terminal çıktısı için değil, dosya içerikleri üretmek için de mükemmeldir. Özellikle konfigürasyon dosyaları veya şablon dosyaları oluştururken:

#!/bin/bash

# Nginx sanal host konfigürasyonu oluştur
create_vhost() {
    local domain="$1"
    local port="${2:-80}"
    local root_dir="${3:-/var/www/$domain}"
    local config_file="/etc/nginx/sites-available/$domain"
    
    printf "server {n" > "$config_file"
    printf "    listen %d;n" "$port" >> "$config_file"
    printf "    server_name %s www.%s;n" "$domain" "$domain" >> "$config_file"
    printf "    root %s;n" "$root_dir" >> "$config_file"
    printf "n" >> "$config_file"
    printf "    access_log /var/log/nginx/%s_access.log;n" "$domain" >> "$config_file"
    printf "    error_log /var/log/nginx/%s_error.log;n" "$domain" >> "$config_file"
    printf "n" >> "$config_file"
    printf "    location / {n" >> "$config_file"
    printf "        try_files $uri $uri/ =404;n" >> "$config_file"
    printf "    }n" >> "$config_file"
    printf "}n" >> "$config_file"
    
    printf "Konfigürasyon oluşturuldu: %sn" "$config_file"
}

create_vhost "example.com" 80 "/var/www/example"

Yaygın Hatalar ve Çözümleri

Hata 1: Yüzde işareti sorunu

# YANLIŞ - hata verir
printf "Kullanım: 75%"

# DOĞRU
printf "Kullanım: 75%%n"
printf "Kullanım: %d%%n" 75

Hata 2: Değişken içinde özel karakterler

# Güvenli string kullanımı
dosya_adi="/tmp/test bos lu k.txt"
printf "Dosya: %sn" "$dosya_adi"  # Tırnak içinde güvenli

# Tehlikeli yol - boşluklar sorun çıkarır
printf "Dosya: $dosya_adin"  # Çalışır ama iyi pratik değil

Hata 3: Sayı biçimi uyumsuzluğu

# Ondalıklı sayıyı %d ile kullanmak
printf "%dn" 3.14  # Hata veya beklenmedik sonuç

# Doğrusu
printf "%.0fn" 3.14  # 3
printf "%fn" 3.14    # 3.140000
printf "%.2fn" 3.14  # 3.14

Hata 4: Negatif genişlik değerleri

# Genişlik değişkenden geliyorsa
genislik=15
printf "%${genislik}sn" "metin"   # Çalışır
printf "%-${genislik}sn" "metin"  # Sola hizalı

Performans Karşılaştırması

Büyük döngülerde printf, echo‘ya göre genellikle daha hızlı çalışır çünkü built-in bir komuttur ve format dizesini optimize edilmiş şekilde işler. Bir test:

# echo ile 10000 satır
time for i in $(seq 1 10000); do
    echo "Satır numarası: $i"
done > /dev/null

# printf ile 10000 satır
time for i in $(seq 1 10000); do
    printf "Satır numarası: %dn" "$i"
done > /dev/null

# Daha da hızlı: printf'in tekrarlama özelliği
time printf "Satır numarası: %dn" $(seq 1 10000) > /dev/null

Özellikle son yöntem, döngü overhead’ını tamamen ortadan kaldırır.

Küçük Ama Kullanışlı Püf Noktaları

Günlük işlerde işinize yarayacak bazı printf teknikleri:

# Belirli sayıda karakter tekrarlama
printf '=%.0s' {1..40}; echo  # 40 tane = işareti

# Hizalanmış sayı listesi
printf "%3d. %sn" 1 "birinci" 2 "ikinci" 10 "onuncu"

# Hex renk kodlarını RGB'ye çevirme
hex_to_rgb() {
    local hex="${1##}"  # # işaretini kaldır
    printf "R:%d G:%d B:%dn" 
        "0x${hex:0:2}" 
        "0x${hex:2:2}" 
        "0x${hex:4:2}"
}
hex_to_rgb "#FF6B35"

# IP adresini ters çevirme (PTR kayıtları için)
ip="192.168.1.100"
IFS='.' read -ra oktets <<< "$ip"
printf "%s.%s.%s.%s.in-addr.arpan" 
    "${oktets[3]}" "${oktets[2]}" "${oktets[1]}" "${oktets[0]}"

Sonuç

printf komutu, terminal dünyasında sadeliğin altında saklanan bir güç aracıdır. echo‘nun hızlıca üstesinden gelemeyeceği biçimlendirme ihtiyaçları ortaya çıktığında, printf tam ihtiyacınız olan esnekliği sunar.

Özellikle şu durumlarda printf‘i tercih etmelisiniz: hizalanmış çıktı üretirken, sayısal verileri belirli hassasiyette gösterirken, standart formatlı log dosyaları yazarken ve script çıktılarını profesyonel görünümlü raporlara dönüştürürken.

awk, sed ve grep gibi araçlarla birlikte kullanıldığında printf, metin işleme pipeline’larının vazgeçilmez bir parçası haline gelir. C programcılarına tanıdık gelen bu sözdizimini bir kez öğrendikten sonra, hem bash scriptlerinde hem de awk bloklarında aynı bilgiyi kullanabilirsiniz.

Pratik yapmanın en iyi yolu, mevcut scriptlerinizdeki echo kullanımlarını printf‘e dönüştürmektir. Her dönüşümde yeni bir şey öğreneceğinizi ve çıktılarınızın ne kadar daha düzenli göründüğünü fark edeceğinizi garanti edebilirim.

Yorum yapın