awk ve sed ile Yapılandırılmış Config Dosyaları Okuma, Güncelleme ve Doğrulama
Yıllarca production sistemlerde çalışmış biri olarak şunu söyleyebilirim: bir config dosyasını yanlış düzenlemek, saatlerce süren bir kesintinin başlangıcı olabilir. Elle düzenleme yaparken vim‘de yanlış satıra gitmek, kopyala-yapıştır hataları, trailing space bırakmak… hepsini yaşadım. Sonra fark ettim ki awk ve sed sadece “metin işleme araçları” değil, aslında config yönetiminin temel silahları. Bu yazıda gerçek dünya senaryolarıyla bu iki aracın config dosyalarıyla nasıl kullanıldığını aktaracağım.
Neden awk ve sed, Neden Başka Bir Şey Değil?
Python yazabilirsiniz, Ansible kullanabilirsiniz, hatta Puppet veya Chef ile tüm config yönetimini otomatize edebilirsiniz. Ama şu anı düşünün: gece 2’de bir alarm geldi, production sunucuda tek bir parametre yanlış, ve sizin önünüzde bare-metal bir terminal var. Ansible playbook çalıştırmak için zaman yok, Python script yazmak için vakit yok. İşte tam o anda sed ve awk sizi kurtarır.
Bu araçların güzelliği şu: her Linux sisteminde varsayılan olarak yüklü gelirler, bağımlılıkları yoktur ve pipe zincirlerinde mükemmel çalışırlar. awk yapılandırılmış verileri alan bazında işlemek için tasarlanmıştır; sed ise satır bazlı dönüşümler için. Config dosyaları da tam olarak bu ikisinin kesişim noktasında yaşar.
Config Dosyası Anatomisi ve Yaklaşım Felsefesi
Bir config dosyasını okumadan önce yapısını anlamak gerekir. Tipik Linux config dosyaları birkaç farklı formatta gelir:
- key=value formatı:
/etc/environment,.envdosyaları, birçok uygulama config’i - key value formatı (eşittir yok):
sshd_config,nginx.confbazı direktifleri - section bazlı INI formatı:
smb.conf,php.ini, bazı Python uygulama config’leri - comment satırlı karışık format: neredeyse tüm gerçek dünya config’leri
Her format için farklı bir yaklaşım gerekmez; awk ve sed kombinasyonu hepsini kapsayabilir. Ama önce okumayı öğrenmek, sonra güncellemeye geçmek mantıklı.
awk ile Config Dosyası Okuma
Temel Değer Okuma
/etc/ssh/sshd_config dosyasından bir değer okumak istediğinizi düşünün. Bu dosyada hem aktif hem yorum satırı olabilir, alanlar boşlukla ayrılmıştır.
# PermitRootLogin değerini oku (yorum satırlarını atla)
awk '!/^#/ && /^PermitRootLogin/ {print $2}' /etc/ssh/sshd_config
# MaxAuthTries değerini oku
awk '!/^#/ && $1 == "MaxAuthTries" {print $2}' /etc/ssh/sshd_config
İkinci örnekteki fark önemli: /MaxAuthTries/ pattern ile $1 == "MaxAuthTries" tam eşleşme farklıdır. MaxAuthTriesPerUser gibi bir parametre varsa ilki onu da yakalar, ikincisi yakalamaz.
key=value Formatından Değer Okuma
.env dosyaları veya /etc/environment gibi = ile ayrılmış dosyalar için awk‘ın field separator özelliği işe yarar:
# FS olarak = kullan, yorum ve boş satırları atla
awk -F'=' '!/^#/ && NF>1 {
key = $1
# Değer kısmında birden fazla = olabilir (örn: base64 string)
val = ""
for(i=2; i<=NF; i++) {
val = (i==2) ? $i : val "=" $i
}
gsub(/^[ t]+|[ t]+$/, "", key)
gsub(/^[ t]+|[ t]+$/, "", val)
print key " --> " val
}' /etc/environment
Bu script biraz daha karmaşık görünüyor ama gerçek dünyada DATABASE_URL=postgres://user:pass@host/db?sslmode=require gibi değerler olduğunda sağ tarafta birden fazla = işareti görebilirsiniz. Basit awk -F'=' '{print $2}' bu durumda sizi yarı yolda bırakır.
INI Formatında Section Bazlı Okuma
php.ini veya smb.conf gibi section’lı dosyalardan belirli bir section’daki değerleri okumak için awk oldukça güçlüdür:
# php.ini'den sadece [opcache] section'ındaki değerleri oku
awk '
/^[/ {
# Section başlığını temizle ve kaydet
gsub(/[[]]/, "")
current_section = $0
}
!/^[/ && !/^;/ && !/^$/ && current_section == "opcache" {
print current_section " | " $0
}
' /etc/php/8.1/fpm/php.ini
Bu yaklaşımı genişletirsek, belirli bir section’daki spesifik bir değeri bulmak da mümkün:
# [mysql] section'ındaki max_connections değerini bul
awk -F'=' '
/^[/ { gsub(/[[]]/, ""); section = $0 }
section == "mysql" && $1 ~ /^[[:space:]]*max_connections/ {
gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2)
print $2
}
' /etc/mysql/mysql.conf.d/mysqld.cnf
sed ile Config Güncelleme
Okumak nispeten kolaydır; asıl mesele güvenli güncelleme. sed ile in-place güncelleme yaparken en büyük kural: her zaman yedek al.
Temel Değer Güncelleme
# sshd_config'de PermitRootLogin değerini güncelle
# -i.bak: orijinal dosyayı .bak uzantısıyla yedekler
sed -i.bak 's/^PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
# Değişikliğin yapıldığını doğrula
grep "PermitRootLogin" /etc/ssh/sshd_config
Ama bu örnekte bir sorun var: eğer PermitRootLogin satırı yorum satırı olarak #PermitRootLogin yes şeklindeyse, bu sed komutu onu yakalamaz. Satırı aktif hale getirmek ve değiştirmek için şöyle yapabilirsiniz:
# Hem yorum hem aktif satırı ele al, tek bir davranışa getir
sed -i.bak '/^#*[[:space:]]*PermitRootLogin/cPermitRootLogin no' /etc/ssh/sshd_config
c komutu tüm satırı yeni içerikle değiştirir. Bu s/// komutundan daha güvenlidir çünkü satırda ne olursa olsun tamamen silerek yenisini yazar.
Eşittir İşaretli Config Güncelleme
# nginx worker_processes değerini güncelle
# Önce dosyanın gerçek yolu ve içeriği kontrol et
sed -i.bak 's/^(worker_processess*)=s*.*/1= 4/' /etc/nginx/nginx.conf
# php.ini'de memory_limit güncelle (noktalı virgüllü yorum satırları hariç)
sed -i.bak '/^[^;]*memory_limit/s/=.*/= 512M/' /etc/php/8.1/fpm/php.ini
İkinci örnekteki yaklaşım dikkat çekici: önce satırı bul (/^[^;]*memory_limit/), sonra sadece o satırda = sonrasını değiştir. Bu şekilde regex’i iki parçaya bölerek hem doğruluk hem okunabilirlik kazanırsınız.
Mevcut Yoksa Ekle, Varsa Güncelle (Upsert Mantığı)
Bu pattern gerçekten çok işe yarar. Bir değer config’de varsa güncelle, yoksa sona ekle:
#!/bin/bash
CONFIG_FILE="/etc/myapp/app.conf"
KEY="max_connections"
VALUE="200"
if grep -q "^${KEY}" "${CONFIG_FILE}"; then
# Satır var, güncelle
sed -i.bak "s/^${KEY}=.*/${KEY}=${VALUE}/" "${CONFIG_FILE}"
echo "Guncellendi: ${KEY}=${VALUE}"
else
# Satır yok, ekle
echo "${KEY}=${VALUE}" >> "${CONFIG_FILE}"
echo "Eklendi: ${KEY}=${VALUE}"
fi
Bu script’i daha da güçlendirerek hem yorum satırlarını hem de boşluk varyasyonlarını ele alabiliriz, ama temel mantık bu.
Section’a Özgü Güncelleme
smb.conf gibi dosyalarda belirli bir section içindeki değeri güncellemek için awk ve sed‘i birlikte kullanmak gerekebilir. Ama sadece awk ile de yazabilirsiniz:
# [global] section'ındaki "workgroup" değerini değiştir
awk '
/^[global]/ { in_section=1 }
/^[/ && !/^[global]/ { in_section=0 }
in_section && /^[[:space:]]*workgroup[[:space:]]*=/ {
sub(/=.*/, "= MYWORKGROUP")
}
{ print }
' /etc/samba/smb.conf > /tmp/smb.conf.new && mv /tmp/smb.conf.new /etc/samba/smb.conf
Bu yaklaşımda dikkat edilmesi gereken nokta: awk doğrudan in-place yazma yapmaz (GNU awk‘ın -i inplace opsiyonu var ama taşınabilirlik istiyorsanız güvenmeyin). Bu yüzden geçici dosyaya yazıp sonra taşımak standart yaklaşımdır.
Config Dosyası Doğrulama
Okuma ve güncelleme kadar önemli bir başka konu: doğrulama. Bir config’i güncelledikten sonra beklenen değerlerin orada olduğunu, format bozulmadığını ve zorunlu parametrelerin mevcut olduğunu kontrol etmek şarttır.
Zorunlu Parametrelerin Varlığını Kontrol Etme
#!/bin/bash
CONFIG_FILE="/etc/myapp/app.conf"
# Zorunlu parametreler listesi
REQUIRED_PARAMS=(
"database_host"
"database_port"
"max_connections"
"log_level"
)
MISSING=0
for param in "${REQUIRED_PARAMS[@]}"; do
if ! awk -F'=' -v p="$param" '
!/^#/ && !/^$/ && $1 ~ "^[[:space:]]*" p "[[:space:]]*$" {found=1}
END {exit !found}
' "$CONFIG_FILE"; then
echo "HATA: Zorunlu parametre eksik: $param"
MISSING=$((MISSING + 1))
fi
done
if [ $MISSING -eq 0 ]; then
echo "Tum zorunlu parametreler mevcut."
else
echo "$MISSING parametre eksik. Config dosyasini kontrol edin."
exit 1
fi
Değer Format Doğrulaması
Bir config dosyasındaki değerlerin beklenen formatta olup olmadığını kontrol etmek de kritik. Örneğin port numarasının gerçekten sayı olduğunu doğrulayalım:
# Port değerlerinin geçerli aralıkta olduğunu kontrol et
awk -F'=' '
!/^#/ && $1 ~ /port/ {
gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2)
port = $2 + 0
if ($2 !~ /^[0-9]+$/) {
print "HATA: " $1 " port degeri sayi degil: " $2
} else if (port < 1 || port > 65535) {
print "HATA: " $1 " port degeri gecersiz aralikta: " port
} else {
print "OK: " $1 " = " port
}
}
' /etc/myapp/app.conf
Duplicate Key Tespiti
Özellikle büyük config dosyalarında aynı anahtarın birden fazla kez tanımlanması sorun yaratabilir. Hangi uygulama hangisini kullanır belli olmaz:
# Tekrarlayan anahtarları bul
awk -F'=' '
!/^#/ && !/^$/ && NF >= 2 {
gsub(/^[[:space:]]+|[[:space:]]+$/, "", $1)
key = $1
count[key]++
lines[key] = lines[key] " " NR
}
END {
for (k in count) {
if (count[k] > 1) {
print "UYARI: "" k "" " count[k] " kez tanimlanmis. Satirlar:" lines[k]
}
}
}
' /etc/myapp/app.conf
Bu script üretim ortamında bize bir keresinde çok ciddi bir problemi ortaya çıkardı. Farklı ekip üyeleri aynı config’e farklı zamanlarda parametre eklemişti ve kimse fark etmemişti.
Gerçek Dünya: Toplu Sunucu Config Güncelleme
Diyelim ki 50 sunucunuzda /etc/sysctl.conf dosyasını güncellemek istiyorsunuz. Her sunucuda vm.swappiness değerini 60‘tan 10‘a indirmeniz gerekiyor. Bu değer bazı sunucularda yorum satırı, bazılarında aktif, bazılarında ise hiç yok.
#!/bin/bash
# sysctl_update.sh - Tum sunucularda swappiness guncelle
TARGET_PARAM="vm.swappiness"
TARGET_VALUE="10"
CONFIG_FILE="/etc/sysctl.conf"
BACKUP_SUFFIX=".bak.$(date +%Y%m%d_%H%M%S)"
update_sysctl_param() {
local file="$1"
local param="$2"
local value="$3"
# Yedek al
cp "${file}" "${file}${BACKUP_SUFFIX}"
# Yorum veya aktif satır var mı kontrol et
if grep -qE "^[[:space:]]*#*[[:space:]]*${param}" "${file}"; then
# Var, aktif yap ve değeri güncelle
sed -i "s|^[[:space:]]*#*[[:space:]]*${param}[[:space:]]*=.*|${param} = ${value}|" "${file}"
else
# Yok, dosyanın sonuna ekle
echo "" >> "${file}"
echo "# Otomatik eklendi: $(date)" >> "${file}"
echo "${param} = ${value}" >> "${file}"
fi
# Doğrulama
actual=$(awk -F'=' -v p="${param}" '
!/^#/ && $1 ~ "^[[:space:]]*" p "[[:space:]]*$" {
gsub(/^[[:space:]]+|[[:space:]]+$/, "", $2)
print $2
exit
}
' "${file}")
if [ "${actual}" = "${value}" ]; then
echo "OK: ${param} = ${actual}"
return 0
else
echo "HATA: Beklenen ${value}, bulunan: ${actual}"
# Hata durumunda yedeği geri yükle
mv "${file}${BACKUP_SUFFIX}" "${file}"
return 1
fi
}
update_sysctl_param "${CONFIG_FILE}" "${TARGET_PARAM}" "${TARGET_VALUE}"
# Başarılı olduysa kernel'e uygula
if [ $? -eq 0 ]; then
sysctl -p "${CONFIG_FILE}"
fi
Bu script’in güzelliği hem idempotent olması (birden fazla çalıştırırsanız sorun çıkarmaz) hem de hata durumunda otomatik geri alma yapması.
sed ile Config Diff ve Değişiklik Takibi
Bir config değişikliği yapmadan önce ve sonra farkı görmek, özellikle audit log tutmak için önemli:
#!/bin/bash
# Config değişikliğini kaydet ve uygula
CONFIG_FILE="/etc/nginx/nginx.conf"
CHANGE_LOG="/var/log/config_changes.log"
# Önceki hash'i al
before_hash=$(md5sum "${CONFIG_FILE}" | awk '{print $1}')
# Değişikliği yap
sed -i.bak 's/worker_processes auto/worker_processes 4/' "${CONFIG_FILE}"
# Sonraki hash'i al
after_hash=$(md5sum "${CONFIG_FILE}" | awk '{print $1}')
# Değişiklik olduysa logla
if [ "${before_hash}" != "${after_hash}" ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') | $(whoami) | ${CONFIG_FILE} degistirildi" >> "${CHANGE_LOG}"
diff "${CONFIG_FILE}.bak" "${CONFIG_FILE}" >> "${CHANGE_LOG}"
echo "---" >> "${CHANGE_LOG}"
fi
Sık Yapılan Hatalar ve Kaçınma Yolları
- Yedek almadan in-place düzenleme:
sed -ikullanırken her zaman-i.bakgibi bir suffix ekleyin ya da önce manuelcpyapın. - Regex’te özel karakterleri escape etmemek: Config değerlerinde
.,,[gibi karakterler olabilir.sedkomutunda bunlarıile escape edin ya da değişkenisediçine geçirirkenprintf '%sn' "$var" | sed 's/[[.^$()+?{|]/\&/g'ile sanitize edin. - Boş dosya riski:
awk '{...} ' file > fileşeklinde aynı dosyaya yazmaya çalışmak dosyayı sıfırlar. Her zaman geçici dosya kullanın. - Encoding sorunları: Özellikle Windows’tan gelmiş config dosyalarında
rnsatır sonları olabilir.awkbazen bunları değerin parçası olarak görür.sed 's/r//' fileile önce temizleyin. - Case sensitivity:
awkvesedvarsayılan olarak büyük/küçük harf duyarlıdır.MaxConnectionsilemaxconnectionsfarklıdır. GerekirseawkiçindeIGNORECASE=1kullanın (GNU awk özelliği).
Sonuç
awk ve sed kombinasyonu, config dosyası yönetiminde size hem hız hem de güvenilirlik sağlar. Ansible veya Terraform gibi araçlar büyük ölçekli otomasyon için vazgeçilmez olsa da, bunların altında yatan mantığı anlamak ve acil durumlarda elle müdahale edebilmek her sistem yöneticisinin repertuvarında olmalıdır.
Bu yazıda ele aldığımız temel prensipler şunlardır:
- Okuma:
awk‘ın field separator ve pattern matching gücünü kullanarak yapılandırılmış veriyi parse etmek - Güncelleme:
sed‘ins///veckomutlarıyla güvenli in-place değişiklik yapmak, upsert pattern’ini uygulamak - Doğrulama: Değişiklikten sonra beklenen değerleri kontrol etmek, duplicate tespiti yapmak, format validasyonu uygulamak
Son olarak şunu hatırlatayım: bu araçları öğrenmenin en iyi yolu gerçek config dosyaları üzerinde pratik yapmaktır. Test ortamınızda bir sshd_config, bir nginx.conf, bir php.ini alın ve bu yazıdaki komutları üzerinde deneyin. Bir süre sonra bu pattern’ler içgüdüsel hale gelir ve artık “bu config’e nasıl dokunurum” yerine “bunu nasıl otomatize ederim” diye düşünmeye başlarsınız. İşte o noktada gerçekten sysadmin olduğunuzu anlarsınız.
