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.