Metin Şablonlarından Dosya Üretme: here-doc Kullanımı

Sistem yöneticiliğinde işlerin tekrar eden kısımlarını otomatize etmek, zamanın büyük bir bölümünü kurtarır. Konfigürasyon dosyaları üretmek, dinamik script’ler oluşturmak ya da toplu kullanıcı bildirimleri göndermek gibi durumlarda sürekli aynı şablon içeriklerini elle yazmak hem zaman kaybı hem de hata kaynağıdır. İşte bu noktada here-doc (heredoc) devreye girer. Bash’in bu yerleşik özelliği, birden fazla satırlı metin bloklarını doğrudan shell script’lerinin içinde tanımlamanıza ve bu blokları dosyaya yazmanıza, komutlara beslemenize ya da değişkenlerle dinamik hale getirmenize olanak tanır.

Here-doc Nedir ve Nasıl Çalışır?

Here-doc, bir komuta veya dosyaya birden fazla satırlık metin aktarmak için kullanılan bir bash yönlendirme mekanizmasıdır. << operatörü ile başlar ve ardından bir sınır belirteç (delimiter) gelir. Bu belirteç, metin bloğunun nerede başlayıp nerede biteceğini Bash’e söyler.

Temel sözdizimi şöyledir:

komut << SINIR_BELIРТECI
satır1
satır2
satır3
SINIR_BELIРТECI

Pratikte en sık kullanılan belirteç EOF (End Of File) ya da EOL (End Of Line) olsa da aslında istediğiniz herhangi bir kelimeyi kullanabilirsiniz. CONF, TEMPLATE, SCRIPT gibi anlamlı belirteçler kodun okunabilirliğini artırır.

Bir dosyaya doğrudan here-doc ile yazmak için yönlendirme operatörünü kullanırsınız:

cat << EOF > /tmp/merhaba.txt
Bu bir here-doc testidir.
Birden fazla satir yazabilirsiniz.
Tarih: $(date)
Kullanici: $USER
EOF

Bu komut çalıştırıldığında /tmp/merhaba.txt dosyası oluşturulur ve içine hem statik metin hem de shell değişkenleri expand edilerek yazılır. $(date) ve $USER otomatik olarak değerlendirilir.

Değişken Genişletme Kontrolü

Here-doc’un en güçlü özelliklerinden biri, değişken genişletmeyi (variable expansion) kontrol edebilmenizdir. Varsayılan davranışta bash değişkenleri ve komut ikamesi otomatik olarak çalışır. Ama bazen bunu istemezsiniz. Örneğin bir PHP veya Python scripti üretirken $degisken ifadesinin bash tarafından yorumlanmasını değil, dosyaya olduğu gibi yazılmasını isteyebilirsiniz.

Değişken genişletmeyi devre dışı bırakmak için başlangıç belirtecini tırnak içine alırsınız:

cat << 'EOF' > /tmp/php_sablonu.php
<?php
$baglanti = new PDO($dsn, $kullanici, $sifre);
$sorgu = $baglanti->prepare("SELECT * FROM users WHERE id = :id");
$sorgu->execute(['id' => $id]);
?>
EOF

Tırnak işareti olmadan bu komut çalıştırılsaydı, $dsn, $kullanici, $sifre gibi değişkenler bash tarafından boş string olarak yorumlanırdı ve üretilen PHP dosyası bozuk olurdu. Tek tırnak ile belirteci çevrelediğinizde hiçbir genişletme yapılmaz, metin olduğu gibi yazılır.

Girintileri Temizlemek: <<- Operatörü

Script içinde okunabilirlik için kodu girintili yazarsınız ama bu girintiler here-doc içeriğine de yansır. <<- operatörü bu sorunu çözer. Yalnızca tab karakterlerini siler (boşluk değil), bu yüzden girintileme için sekme kullanmanız gerekir.

#!/bin/bash

olustur_nginx_konfig() {
    local domain=$1
    local port=$2

    cat <<- NGINX_CONF > /etc/nginx/sites-available/$domain
	server {
	    listen 80;
	    server_name $domain www.$domain;

	    location / {
	        proxy_pass http://127.0.0.1:$port;
	        proxy_set_header Host $host;
	        proxy_set_header X-Real-IP $remote_addr;
	    }
	}
	NGINX_CONF

    echo "$domain icin Nginx konfigurasyonu olusturuldu."
}

olustur_nginx_konfig "orneksite.com" "3000"

Burada $host ifadesine dikkat edin. $host Nginx’in kendi değişkeni, bash’in değil. Eğer backslash koymazsanız bash bunu kendi değişkeni sanıp boş string’e çevirir. Değişken çakışmalarına karşı dikkatli olmak gerekir.

Gerçek Dünya Senaryosu 1: Toplu Kullanıcı Kurulum Scripti

Yeni sunuculara kurulum yapıyorsunuz ve her seferinde aynı konfigürasyon dosyalarını manuel oluşturuyorsunuz. Here-doc ile bunu tamamen otomatize edebilirsiniz:

#!/bin/bash

KULLANICI_ADI=$1
PROJE_ADI=$2
APP_PORT=$3

if [ -z "$KULLANICI_ADI" ] || [ -z "$PROJE_ADI" ] || [ -z "$APP_PORT" ]; then
    echo "Kullanim: $0 <kullanici_adi> <proje_adi> <port>"
    exit 1
fi

PROJE_DIZINI="/home/$KULLANICI_ADI/projects/$PROJE_ADI"

# Proje dizinini olustur
mkdir -p "$PROJE_DIZINI"

# Systemd servis dosyasi olustur
cat << SYSTEMD > /etc/systemd/system/${PROJE_ADI}.service
[Unit]
Description=$PROJE_ADI Uygulamasi
After=network.target

[Service]
Type=simple
User=$KULLANICI_ADI
WorkingDirectory=$PROJE_DIZINI
ExecStart=/usr/bin/node $PROJE_DIZINI/index.js
Restart=on-failure
RestartSec=10
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=$PROJE_ADI
Environment=NODE_ENV=production
Environment=PORT=$APP_PORT

[Install]
WantedBy=multi-user.target
SYSTEMD

# .env dosyasi olustur
cat << ENV > $PROJE_DIZINI/.env
NODE_ENV=production
PORT=$APP_PORT
LOG_LEVEL=info
APP_NAME=$PROJE_ADI
CREATED_AT=$(date '+%Y-%m-%d %H:%M:%S')
ENV

# Logrotate konfigurasyonu
cat << LOGROTATE > /etc/logrotate.d/$PROJE_ADI
/var/log/$PROJE_ADI/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 0640 $KULLANICI_ADI adm
    sharedscripts
    postrotate
        systemctl reload $PROJE_ADI > /dev/null 2>&1 || true
    endscript
}
LOGROTATE

echo "Kurulum tamamlandi:"
echo "  Servis dosyasi: /etc/systemd/system/${PROJE_ADI}.service"
echo "  Env dosyasi: $PROJE_DIZINI/.env"
echo "  Logrotate: /etc/logrotate.d/$PROJE_ADI"

systemctl daemon-reload
systemctl enable $PROJE_ADI
echo "Servis etkinlestirildi. Baslatmak icin: systemctl start $PROJE_ADI"

Bu script tek bir komutla (./kur.sh webadmin blogapp 3001) üç farklı konfigürasyon dosyası oluşturur ve servisi sisteme tanıtır. Yeni bir uygulama deploy etmeniz gereken her seferinde kullanabilirsiniz.

Gerçek Dünya Senaryosu 2: SSH Config Şablonu

Onlarca sunucu yönetiyorsanız SSH konfigürasyonları baş ağrısına dönüşebilir. Here-doc ile dinamik SSH config bloğu oluşturabilirsiniz:

#!/bin/bash

ekle_ssh_host() {
    local takim_adi=$1
    local ip=$2
    local kullanici=$3
    local anahtar_dosyasi=${4:-"~/.ssh/id_rsa"}

    cat << SSH_CONF >> ~/.ssh/config

# $takim_adi - $(date '+%Y-%m-%d tarihinde eklendi')
Host $takim_adi
    HostName $ip
    User $kullanici
    IdentityFile $anahtar_dosyasi
    ServerAliveInterval 60
    ServerAliveCountMax 3
    StrictHostKeyChecking no
    Compression yes
SSH_CONF

    echo "$takim_adi ($ip) SSH config dosyasina eklendi."
}

# Kullanim ornekleri
ekle_ssh_host "prod-web01" "10.0.1.10" "deploy" "~/.ssh/prod_rsa"
ekle_ssh_host "prod-db01" "10.0.1.20" "dbadmin" "~/.ssh/prod_rsa"
ekle_ssh_host "staging-web" "10.0.2.10" "deploy" "~/.ssh/staging_rsa"

>> ile mevcut dosyaya ekleme yapıyoruz, > ile üzerine yazıyoruz. Bu ayrımı asla karıştırmayın, özellikle SSH config dosyaları için.

Metin İşleme Komutlarıyla Birlikte Kullanım

Here-doc’u sed, awk ve grep gibi araçlarla birleştirdiğinizde gerçekten güçlü şablonlama mekanizmaları oluşturabilirsiniz.

sed ile placeholder değiştirme:

Bazen daha karmaşık şablon sistemlerine ihtiyaç duyarsınız. Bash değişkenlerinin yetersiz kaldığı durumlarda, şablonu önce sabit placeholder’larla yazıp sonra sed ile değiştirme yapabilirsiniz:

#!/bin/bash

DOMAIN=$1
SSL_CERT_PATH="/etc/ssl/certs/${DOMAIN}.crt"
SSL_KEY_PATH="/etc/ssl/private/${DOMAIN}.key"

# Once sablon dosyasini olustur
cat << 'NGINX_TEMPLATE' > /tmp/nginx_sablon.conf
server {
    listen 80;
    server_name __DOMAIN__ www.__DOMAIN__;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name __DOMAIN__ www.__DOMAIN__;

    ssl_certificate __SSL_CERT__;
    ssl_certificate_key __SSL_KEY__;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;

    access_log /var/log/nginx/__DOMAIN___access.log;
    error_log /var/log/nginx/__DOMAIN___error.log;

    location / {
        root /var/www/__DOMAIN__;
        index index.html index.php;
        try_files $uri $uri/ =404;
    }
}
NGINX_TEMPLATE

# sed ile placeholder'lari gercek degerlerle degistir
sed 
    -e "s|__DOMAIN__|${DOMAIN}|g" 
    -e "s|__SSL_CERT__|${SSL_CERT_PATH}|g" 
    -e "s|__SSL_KEY__|${SSL_KEY_PATH}|g" 
    /tmp/nginx_sablon.conf > /etc/nginx/sites-available/${DOMAIN}.conf

# Gecici dosyayi temizle
rm /tmp/nginx_sablon.conf

echo "${DOMAIN} icin Nginx SSL konfigurasyonu hazirlandi."
nginx -t && systemctl reload nginx

Bu yaklaşımın avantajı, şablonu tek tırnaklı here-doc ile yazıp bash genişletmesini tamamen kapatmanız ve ardından kontrollü bir şekilde sed ile yalnızca istediğiniz değerleri değiştirmenizdir. $server_name ve $request_uri gibi Nginx değişkenleri bu sayede korunur.

awk ile Dinamik Rapor Üretimi

awk içinde here-doc kullanarak log dosyalarından otomatik HTML raporlar üretebilirsiniz:

#!/bin/bash

LOG_DOSYASI="/var/log/nginx/access.log"
RAPOR_DOSYASI="/var/www/html/rapor.html"
TARIH=$(date '+%d %B %Y, %H:%M')
TOPLAM_ISTEK=$(wc -l < "$LOG_DOSYASI")
HATA_SAYISI=$(grep -c " 5[0-9][0-9] " "$LOG_DOSYASI")
BASARILI=$(grep -c " 200 " "$LOG_DOSYASI")

# HTML raporun basligini here-doc ile olustur
cat << HTML_HEADER > "$RAPOR_DOSYASI"
<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <title>Gunluk Erisim Raporu - $TARIH</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        table { border-collapse: collapse; width: 100%; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background-color: #4CAF50; color: white; }
        .error { color: red; font-weight: bold; }
    </style>
</head>
<body>
<h1>Nginx Erisim Raporu</h1>
<p>Olusturulma tarihi: <strong>$TARIH</strong></p>
<h2>Ozet</h2>
<ul>
    <li>Toplam istek: <strong>$TOPLAM_ISTEK</strong></li>
    <li>Basarili (200): <strong>$BASARILI</strong></li>
    <li>Sunucu hatasi (5xx): <strong class="error">$HATA_SAYISI</strong></li>
</ul>
<h2>En Cok Erisilen URL'ler</h2>
<table>
<tr><th>URL</th><th>Istek Sayisi</th></tr>
HTML_HEADER

# awk ile en cok erisilen URL'leri tabloya ekle
awk '{print $7}' "$LOG_DOSYASI" | 
    sort | uniq -c | sort -rn | head -20 | 
    awk '{printf "<tr><td>%s</td><td>%s</td></tr>n", $2, $1}' >> "$RAPOR_DOSYASI"

# HTML raporun kapanisini here-doc ile ekle
cat << HTML_FOOTER >> "$RAPOR_DOSYASI"
</table>
</body>
</html>
HTML_FOOTER

echo "Rapor olusturuldu: $RAPOR_DOSYASI"

Here-doc’u Fonksiyonlarda Kullanmak

Here-doc’un en temiz kullanımlarından biri, uzun yardım metinlerini fonksiyonlar içinde sunmaktır:

#!/bin/bash

kullanim_goster() {
    cat << YARDIM
Kullanim: $(basename $0) [SECENEKLER] <hedef>

ACIKLAMA:
    Bu script sunucu konfigurasyonlarini otomatik olusturur.

SECENEKLER:
    -u, --kullanici     Hedef kullanici adi (zorunlu)
    -p, --port          Uygulama portu (varsayilan: 3000)
    -t, --tip           Uygulama tipi: node|python|php (varsayilan: node)
    -s, --ssl           SSL konfigurasyonu ekle
    -h, --yardim        Bu yardim metnini goster

ORNEKLER:
    $(basename $0) -u webadmin -p 8080 -t node ornekapp
    $(basename $0) -u deployer --ssl -t php magazaapp
    $(basename $0) --yardim

NOTLAR:
    - Script root yetkisi gerektirir.
    - Mevcut konfigurasyonlarin uzerine yazmaz.
    - Detayli log icin /var/log/konfigmanager.log dosyasini inceleyin.

Sorunlar icin: [email protected]
YARDIM
}

# Parametre yoksa yardimi goster
if [ $# -eq 0 ]; then
    kullanim_goster
    exit 0
fi

Bu yöntem, yardım metnini ayrı bir dosyada tutmak yerine scriptin içinde tutmanızı sağlar ve script taşınabilirliğini artırır.

Sık Yapılan Hatalar ve Dikkat Edilmesi Gerekenler

Here-doc kullanırken belirli hatalar çok sık karşılaşılır. Bunları önceden bilmek zaman kaybetmenizi önler.

Kapanış belirtecinin başında boşluk olmamalı:

# YANLIS - kapanış belirtecinden önce boşluk var
cat << EOF
merhaba dunya
 EOF   # Bu kapanış olarak tanınmaz!

# DOGRU
cat << EOF
merhaba dunya
EOF

<<- operatöründe boşluk değil tab kullanın:

<<- operatörü yalnızca tab karakterlerini siler. Editörünüz tab’ları otomatik olarak boşluğa çeviriyorsa bu operatör işe yaramaz. Vim kullanıyorsanız :set noexpandtab ile tab genişletmeyi kapatabilirsiniz.

Değişken çakışmalarına dikkat edin:

Nginx, Apache, PHP, systemd gibi araçların kendi değişken sözdizimleri bash ile çakışabilir. $host, $request_uri, $1, ${PORT} gibi ifadeler her iki tarafta da anlam taşıyabilir. Ya tek tırnaklı belirteç kullanıp sed ile placeholder değiştirin, ya da bash değişkenlerini backslash ile kaçış karakteri ekleyerek koruyun ($host).

Dosya izinlerini unutmayın:

# Script dosyasi olusturup calistirılabilir yapma
cat << SCRIPT > /usr/local/bin/yedek_al
#!/bin/bash
rsync -av /var/www/ /backup/www/
SCRIPT

chmod +x /usr/local/bin/yedek_al

Script dosyaları oluştururken chmod yapmayı unutmak klasik bir hatadır.

Pipe ile Here-doc Kullanımı

Here-doc çıktısını doğrudan komutlara pipe’layabilirsiniz. Bu özellikle uzak sunucularda komut çalıştırmak için son derece kullanışlıdır:

#!/bin/bash

UZAK_SUNUCU=$1
KULLANICI=$2

# Uzak sunucuda here-doc ile script calistir
ssh ${KULLANICI}@${UZAK_SUNUCU} << UZAK_KOMUTLAR
    echo "Sistem bilgisi toplanıyor..."
    hostname
    uname -a
    df -h
    free -m
    uptime
    echo "---"
    echo "Son 5 hata logu:"
    journalctl -p err -n 5 --no-pager
UZAK_KOMUTLAR

Bu yöntemde bash değişkenleri $1 ve $2 lokal makinede genişletilir. UZAK_KOMUTLAR belirtecini tırnak içine alırsanız uzak makinedeki değişkenler de korunur ve uzak shell tarafından yorumlanır.

Sonuç

Here-doc, sistem yöneticilerinin araç kutusunda haksız yere göz ardı edilen bir özelliktir. Öğrenmesi birkaç saati almaz ama getirisi muazzamdır. Konfigürasyon dosyaları, servis tanımları, şablon script’ler, HTML raporlar ve yardım metinleri gibi her türlü çok satırlı içeriği tek bir bash script’i içinde yönetebilirsiniz.

Temel prensipleri özetlemek gerekirse:

  • << EOF: Değişken genişletme açık, her şey bash tarafından yorumlanır.
  • << 'EOF': Değişken genişletme kapalı, metin olduğu gibi yazılır.
  • <<- EOF: Tab girintileri temizlenir, okunabilirlik artar.
  • sed ile kombinasyon: Karmaşık şablonlarda placeholder yaklaşımı daha güvenlidir.
  • Kapanış belirteci: Her zaman satır başında, önünde boşluk olmadan yazılmalıdır.

Büyük ölçekli ortamlarda Ansible, Chef ya da Puppet gibi araçlar metin şablonlamasını çok daha sofistike bir şekilde yapabilir. Ama onlarca sunucuya yönelik basit otomasyon görevlerinde, özellikle ekstra bağımlılık istemediğiniz durumlarda, here-doc ile yazılmış bir bash scripti son derece yeterli ve taşınabilir bir çözüm sunar. Sıfır bağımlılık, sıfır kurulum, maksimum kontrol.

Yorum yapın