Mühendislik Verimliligi · Sürdürülebilir Tasarim · Üretime Hazir Otomasyon
- Bash Scripting Temelleri
- Değişkenler ve Veri Tipleri
- Kontrol Yapıları
- Döngüler ve İterasyon
- Diziler ve Gelişmiş Veri Yapıları
- Fonksiyonlar ve Kapsam
- Hata Yönetimi ve Çıkış Kodları
- Regular Expressions (Regex) Ustalığı
- Dosya İşlemleri ve Metin İşleme
- Pratik DevOps Senaryoları
- AI Agent Entegrasyon Kalıpları
- Mühendislik Verimliliği Prensipleri
- Sürdürülebilir Tasarım Kalıpları
- Referanslar
Shebang (#!), çekirdeğe hangi yorumlayıcının kullanılacağını söyleyen sihirli bir bayt dizisidir. Script'in ilk satırı olmak zorundadır — öncesinde boş satır olamaz.
#!/bin/bash
#!/usr/bin/env bash # Taşınabilir — $PATH üzerinden arar
#!/bin/sh # POSIX uyumlu (minimalist)
#!/usr/bin/env python3En İyi Uygulama: Farklı sistemlerde
bash'in farklı yollarda olabileceği göz önüne alındığında, her zaman#!/usr/bin/env bashkullanın.
chmod +x script.sh
chmod 755 script.sh # rwxr-xr-x — script'ler için standart./script.sh # Çalıştırma izni gerektirir
bash script.sh # +x olmadan da çalışır
source script.sh # (veya . script.sh) — mevcut kabukta çalıştırır#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
# ---------------------------------------------------------------
# Script: template.sh
# Açıklama: Üretime hazir bash script şablonu
# Yazar: DevOps Mühendisi
# Kullanim: ./template.sh [secenekler]
# ---------------------------------------------------------------
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly TIMESTAMP="$(date +'%Y-%m-%d_%H-%M-%S')"
readonly SCRIPT_NAME="$(basename "${BASH_SOURCE[0]}")"
# Loglama fonksiyonu
log() {
local seviye="$1"
shift
echo "[${TIMESTAMP}] [${seviye}] $*" >&2
}
main() {
log "BILGI" "Script başlatildi"
# Ana mantik buraya
log "BILGI" "Script tamamlandi"
}
main "$@"# Tek satirlik yorum
: '
Çok satirli
yorum
'isim="Berat" # = etrafinda boşluk OLMAZ
yas=30
readonly PI=3.14159 # Değiştirilemez sabit
local degisken="deger" # Fonksiyon kapsaminda değişken
declare -i sayi=42 # Tam sayı olarak tanımla
declare -r SABIT=1 # Salt okunur (readonly ile aynı)
declare -a dizi=() # Dizi
declare -A harita=() # İlişkisel dizi (hash map)echo "$isim" # Basit ikame — HER ZAMAN TIRNAK İÇİNE ALIN
echo "${isim}" # Süslü parantez — belirsizliği önler
echo "${isim:-varsayilan}" # Tanımlı değilse varsayılan değer
echo "${isim:=varsayilan}" # Tanımlı değilse ata ve kullan
echo "${isim:?hata}" # Tanımlı değilse hata ver
echo "${isim:+alternatif}" # Tanımlıysa alternatif değer
echo "${#isim}" # String uzunluğu
echo "${isim:0:3}" # Alt string (konum, uzunluk)
echo "${isim//e/a}" # Tüm 'e' karakterlerini 'a' ile değiştir
echo "${isim^^}" # Büyük harfe çevir (bash 4+)
echo "${isim,,}" # Küçük harfe çevir (bash 4+)| Değişken | Amaç |
|---|---|
$0 |
Script adı |
$1 – $9 |
Konumsal parametreler (argümanlar) |
${10} |
10. argüman (>9 için süslü parantez şart) |
$# |
Argüman sayısı |
$@ |
Tüm argümanlar (dizi benzeri, her biri ayrı) |
$* |
Tüm argümanlar (tek string) |
$? |
Son komutun çıkış durumu |
$$ |
Mevcut işlem PID'si |
$! |
Son arkaplan işleminin PID'si |
$- |
Mevcut kabuk bayrakları |
$_ |
Önceki komutun son argümanı |
$LINENO |
Mevcut satır numarası |
$SECONDS |
Script çalışma süresi (saniye) |
$RANDOM |
Rastgele tamsayı 0–32767 |
$REPLY |
Varsayılan read değişkeni |
read -p "Adınızı girin: " isim
read -s -p "Şifre: " sifre # Sessiz (ekrana yazılmaz)
read -t 5 -p "Hızlı cevap: " cevap # 5 saniye zaman aşımı
read -n 1 -p "E/H: " onay # Tek karakter, Enter gerekmez
read -a dizi -p "Sayılar: " # Diziye oku# Tamsayı aritmetiği — 3 eşdeğer sözdizimi:
sonuc=$(( 5 + 3 )) # Modern POSIX
sonuc=$[ 5 + 3 ] # Eski (kullanmayın)
let "sonuc = 5 + 3" # let anahtar kelimesi
# Operatörler:
# +, -, *, /, % (mod), ** (üstel)
# +=, -=, *=, /=, %=
# ++, -- (artırım/azaltım)
# &, |, ^, ~, <<, >> (bit düzeyinde)
# Ondalıklı sayı — harici araçlar gerekli:
sonuc=$(echo "scale=2; 5/3" | bc)
sonuc=$(awk 'BEGIN { printf "%.2f", 5/3 }')
# Aritmetik karşılaştırma:
(( 5 > 3 )) && echo "doğru"
(( sonuc++ ))degisken="hello world"
echo "$degisken" # "hello world" — değişken genişletilir
echo '$degisken' # $degisken — literal string (güçlü tırnak)
echo "\$degisken" # $degisken — kaçırılmış (zayıf tırnak)
echo `date` # date komutunu çalıştırır (ters tırnak — kullanmayın)
echo "$(date)" # date komutunu çalıştırır (modern — tercih edin)Altın Kural: Değişken genişletmelerini her zaman çift tırnak içine alın (
"$degisken"). Bu, kelime bölünmesini (word-splitting) ve glob genişlemesini önler. Tırnaksız$degisken, bash hatalarının 1 numaralı kaynağıdır.
if [[ koşul ]]; then
# komutlar
elif [[ diğer_koşul ]]; then
# komutlar
else
# komutlar
fiHer zaman
[[ ]](çift köşeli parantez) kullanın,[ ](tek köşeli parantez) veyatestyerine. Çift köşeli parantezler bash'in yerleşik yapılarıdır ve daha fazla özellik sunar, daha az sürprizle karşılaşırsınız.
[[ -f "$dosya" ]] # Dosya var ve normal bir dosya
[[ -d "$dizin" ]] # Dizin var
[[ -e "$yol" ]] # Yol var
[[ -s "$dosya" ]] # Dosya var ve boş değil
[[ -L "$dosya" ]] # Dosya sembolik bağ
[[ -r "$dosya" ]] # Dosya okunabilir
[[ -w "$dosya" ]] # Dosya yazılabilir
[[ -x "$dosya" ]] # Dosya çalıştırılabilir
[[ -O "$dosya" ]] # Mevcut kullanıcıya ait
[[ -G "$dosya" ]] # Mevcut gruba ait
[[ "$d1" -nt "$d2" ]] # d1, d2'den daha yeni
[[ "$d1" -ot "$d2" ]] # d1, d2'den daha eski[[ -z "$str" ]] # String boş (uzunluk sıfır)
[[ -n "$str" ]] # String dolu (uzunluk sıfır değil)
[[ "$str1" == "$str2" ]] # Stringler eşit
[[ "$str1" != "$str2" ]] # Stringler eşit değil
[[ "$str1" =~ kalıp ]] # Regex eşleşmesi (GENİŞLETİLMİŞ)
[[ "$str1" < "$str2" ]] # Sözlüksel olarak küçük
[[ "$str1" > "$str2" ]] # Sözlüksel olarak büyük
[[ "$str" == *"alt"* ]] # Joker karakter (alt string içerir)
[[ "$str" == önek* ]] # Önek ile başlar
[[ "$str" == *sonek ]] # Sonek ile biter[[ "$a" -eq "$b" ]] # Eşit
[[ "$a" -ne "$b" ]] # Eşit değil
[[ "$a" -lt "$b" ]] # Küçük
[[ "$a" -le "$b" ]] # Küçük veya eşit
[[ "$a" -gt "$b" ]] # Büyük
[[ "$a" -ge "$b" ]] # Büyük veya eşit
# (( )) içinde de geçerlidir:
(( a == b ))
(( a < b ))
(( a >= b ))[[ koşul1 && koşul2 ]] # VE
[[ koşul1 || koşul2 ]] # VEYA
[[ ! koşul ]] # DEĞİLcase "$deger" in
basla|başlat)
echo "Başlatılıyor..."
;;
dur|durdur)
echo "Durduruluyor..."
;;
yeniden|restart)
echo "Yeniden başlatılıyor..."
;;
[0-9][0-9])
echo "İki basamaklı sayı"
;;
*.txt)
echo "Metin dosyası"
;;
"")
echo "Boş string"
;;
*)
echo "Varsayılan durum — diğer her şey"
exit 1
;;
esac# C-tarzı
for (( i=0; i<10; i++ )); do
echo "$i"
done
# Liste-tarzı
for meyve in elma armut muz; do
echo "$meyve"
done
# Aralık (bash 3+)
for i in {1..10}; do
echo "$i"
done
# Aralık adımlı
for i in {0..20..2}; do
echo "$i"
done
# Komut çıktısı
for dosya in $(ls *.txt); do # HATA: boşlukta kelime bölünmesi
for dosya in *.txt; do # DOĞRU: glob genişlemesi güvenlidir
for dosya in /var/log/*.log; do # En iyisi: tam yol glob# Sayaç döngüsü
sayac=0
while [[ $sayac -lt 10 ]]; do
echo "$sayac"
(( sayac++ ))
done
# Dosyayı satır satır oku (EN İYİ yöntem)
while IFS= read -r satir; do
echo "$satir"
done < "girdi.txt"
# Komut çıktısından oku
bazi_komut | while read -r satir; do
echo "$satir"
done
# Sonsuz döngü break ile
while true; do
read -p "Komut girin: " komut
[[ "$komut" == "çık" ]] && break
donesayac=0
until [[ $sayac -ge 10 ]]; do
echo "$sayac"
(( sayac++ ))
done
# Servis ayağa kalkana kadar bekle
until curl -sf http://localhost:8080/saglik; do
echo "Servis bekleniyor..."
sleep 2
donebreak # Döngüden hemen çık
break N # N seviye iç içe döngüden çık
continue # Sonraki iterasyona atla
continue N # N. döngünün sonraki iterasyonuna atlaPS3="Bir seçenek seçin: "
secenekler=("Başlat" "Durdur" "Yeniden Başlat" "Çık")
select secim in "${secenekler[@]}"; do
case "$secim" in
"Başlat") echo "Başlatılıyor..." ;;
"Durdur") echo "Durduruluyor..." ;;
"Yeniden Başlat") echo "Yeniden başlatılıyor..." ;;
"Çık") break ;;
*) echo "Geçersiz seçenek" ;;
esac
done# Tanımlama
dizi=("elma" "armut" "kiraz")
dizi[3]="hurma"
declare -a meyveler=()
# İndeksleme
echo "${dizi[0]}" # İlk eleman
echo "${dizi[-1]}" # Son eleman (bash 4.3+)
echo "${dizi[@]}" # Tüm elemanlar
echo "${dizi[*]}" # Tüm elemanlar tek string
echo "${#dizi[@]}" # Dizi uzunluğu
echo "${!dizi[@]}" # Tüm indeksler (0, 1, 2, ...)
# Dilimleme
echo "${dizi[@]:1:2}" # İndeks 1'den başla, 2 eleman al
# Eleman Ekleme/Değiştirme
dizi+=("mürver")
dizi[1]="yaban mersini"
unset dizi[2] # Eleman sil (boşluk bırakır)
# Döngü
for eleman in "${dizi[@]}"; do
echo "$eleman"
donedeclare -A yapilandirma
yapilandirma=(
["sunucu"]="localhost"
["port"]=8080
["kullanici"]="admin"
)
echo "${yapilandirma[sunucu]}" # localhost
echo "${yapilandirma[@]}" # Tüm değerler
echo "${!yapilandirma[@]}" # Tüm anahtarlar
echo "${#yapilandirma[@]}" # Kayıt sayısı
for anahtar in "${!yapilandirma[@]}"; do
echo "$anahtar => ${yapilandirma[$anahtar]}"
done# String'i diziye böl
IFS=',' read -ra parcaciklar <<< "a,b,c,d"
# Diziyi string'e birleştir
birlestir() {
local IFS="$1"
shift
echo "$*"
}
sonuc=$(birlestir "," "${dizi[@]}")
# Dizi elemanlarını dönüştür
ikile=()
for deger in "${orijinal[@]}"; do
ikile+=("$(( deger * 2 ))")
done
# Eleman dizide var mı kontrol et
dizide_mi() {
local aranan="$1"
shift
for eleman; do
[[ "$eleman" == "$aranan" ]] && return 0
done
return 1
}# Stil 1 (POSIX uyumlu)
fonksiyonum() {
echo "Merhaba fonksiyonum'dan"
}
# Stil 2 (bash'e özel)
function fonksiyonum {
echo "Merhaba fonksiyonum'dan"
}
# Stil 3 (karma — en okunabilir)
function fonksiyonum() {
echo "Merhaba fonksiyonum'dan"
}selamla() {
local isim="$1"
local selam="${2:-Merhaba}"
echo "$selam, $isim!"
}
selamla "Dünya" # Merhaba, Dünya!
selamla "Ali" "Selam" # Selam, Ali!# Çıkış kodu döndür (0–255)
cift_mi() {
[[ $(( $1 % 2 )) -eq 0 ]] && return 0 || return 1
}
if cift_mi 42; then
echo "Çift"
fi
# stdout üzerinden veri döndür ($() ile yakala)
tarih_al() {
date +'%Y-%m-%d'
}
bugun=$(tarih_al)
# Global değişkenle veri döndür
sonuc=""
hesapla() {
sonuc=$(( $1 * $2 ))
}# Değişkenleri her zaman yerelleştirin
dosya_isle() {
local dosya="$1" satir sayac=0
while IFS= read -r satir; do
(( sayac++ ))
done < "$dosya"
echo "$sayac"
}
# Kendini belgeleyen fonksiyonlar
#
# Kullanım: dosya_yedekle <kaynak_yol> [hedef_dizin]
# kaynak_yol: Dosyanın mutlak yolu
# hedef_dizin: Yedek dizini (varsayılan: /tmp/backup)
# Döndürür: 0 başarılı, 1 başarısız
dosya_yedekle() {
local kaynak="$1"
local hedef="${2:-/tmp/backup}"
# ...
}
# Fonksiyon kütüphanesi yükle
source /usr/local/lib/bash-lib.sh# lib/loglama.sh
log_bilgi() { echo "[BILGI] $(date '+%H:%M:%S') $*" >&2; }
log_uyari() { echo "[UYARI] $(date '+%H:%M:%S') $*" >&2; }
log_hata() { echo "[HATA] $(date '+%H:%M:%S') $*" >&2; }
# Ana script'te:
source "$(dirname "$0")/lib/loglama.sh"set -e # İlk hatada çık (errexit)
set -u # Tanımsız değişkenleri hata kabul et (nounset)
set -o pipefail # Pipeline herhangi bir komut başarısız olursa başarısız
# Birleşik (her üretim script'inin başına koyun):
set -euo pipefail| Bayrak | Etkisi | Neden Kullanmalı? |
|---|---|---|
-e |
Script komut hatasında çıkar | Sessiz hataların zincirleme yayılmasını önler |
-u |
Tanımsız değişkenlerde hata verir | Yazım hatalarını yakalar, rm -rf $tanimsiz felaketlerini önler |
-o pipefail |
Pipeline dönüş kodu = son sıfır olmayan çıkış | Olmazsa false | true başarılı olur |
exit 0 # Başarılı
exit 1 # Genel hata
exit 2 # Kabuk yerleşiklerinin yanlış kullanımı
exit 126 # Çalıştırılamayan komut
exit 127 # Komut bulunamadı
exit 128 # exit'e geçersiz argüman
exit 130 # Script Ctrl+C ile sonlandırıldı (128 + SIGINT)
exit 137 # Öldürüldü (SIGKILL)
exit 255 # Çıkış kodu aralık dışı# Çıkışta temizlik
temizlik() {
rm -f /tmp/gecici_$$
log "BILGI" "Temizlik tamamlandı"
}
trap temizlik EXIT
# SIGINT'i (Ctrl+C) yoksay
trap '' SIGINT SIGTERM
# Özel sinyal işleyici
sinyal_isleyici() {
log "UYARI" "Sinyal alındı $1, nazikçe kapatılıyor..."
exit 0
}
trap 'sinyal_isleyici SIGTERM' SIGTERM
trap 'sinyal_isleyici SIGINT' SIGINT
# Hata ayıklama için trap
trap 'echo "$LINENO satirinda HATA (çıkış kodu: $?)"' ERR# Koruma (guard) cümleciği kalıbı
[[ -f "$yapilandirma_dosyasi" ]] || {
echo "HATA: Yapılandırma dosyası bulunamadı: $yapilandirma_dosyasi" >&2
exit 1
}
# Komut durumu kontrolü
if ! mkdir -p "$yedek_dizini"; then
echo "HATA: Yedek dizini oluşturulamadı" >&2
exit 1
fi
# Beklenen hatalar için || true kullan
rm -f "$kilit_dosyasi" || true # Dosya yoksa sorun değil
# Strict modu geçici olarak devre dışı bırak
set +e
hatan_olabilecek_komut
durum=$?
set -e
# Kapsamlı hata yönetimi için alt kabuk kullan
(
set -e
riskli_is_leri_yap
)
echo "Alt kabuk başarısız olsa bile devam ediyoruz"Regular expression (düzenli ifade), bir arama kalıbını tanımlayan karakter dizisidir. Regex "eşleşme" temelinde çalışır — kod çalıştırmaz, metin kalıplarını tanımlar.
Çoğu karakter regex'te kendisiyle eşleşir:
merhaba → "merhaba" metnini herhangi bir yerde eşleştirir
| Meta-kar | Adı | Eşleştiği |
|---|---|---|
. |
Nokta | Yeni satır hariç herhangi bir karakter |
^ |
Şapka | Satır/string başlangıcı |
$ |
Dolar | Satır/string sonu |
* |
Yıldız/Kleene | Önceki elemanın sıfır veya daha fazlası |
+ |
Artı | Önceki elemanın bir veya daha fazlası |
? |
Soru | Önceki elemanın sıfır veya bir tane (opsiyonel) |
\ |
Ters bölü | Kaçış karakteri / literalleri meta'ya çevirir |
| |
Boru | Alternatif (VEYA) |
() |
Parantez | Gruplama ve yakalama |
[] |
Köşeli parantez | Karakter sınıfı (küme) |
{} |
Süslü parantez | Miktar belirleyici |
[abc] # a, b veya c
[a-z] # Herhangi bir küçük harf
[A-Z] # Herhangi bir büyük harf
[0-9] # Herhangi bir rakam
[a-zA-Z] # Herhangi bir harf
[a-zA-Z0-9] # Herhangi bir alfanümerik
[^abc] # a, b veya c DEĞİL (olumsuzlama)
[^0-9] # Rakam DEĞİL
# Ön tanımlı sınıflar (POSIX):
[[:alnum:]] # Alfanümerik: [a-zA-Z0-9]
[[:alpha:]] # Alfabetik: [a-zA-Z]
[[:digit:]] # Rakam: [0-9]
[[:lower:]] # Küçük harf: [a-z]
[[:upper:]] # Büyük harf: [A-Z]
[[:space:]] # Boşluk karakteri: space, tab, newline
[[:blank:]] # Sadece space ve tab
[[:punct:]] # Noktalama
[[:print:]] # Yazdırılabilir karakterler
[[:graph:]] # Görünür karakterler (boşluksuz)
[[:cntrl:]] # Kontrol karakterleri^Merhaba # Satır "Merhaba" ile başlar
Dünya$ # Satır "Dünya" ile biter
^Sadece Bu$ # Tam satır eşleşmesi
^$ # Boş satırcolou?r # "color" veya "colour" (o opsiyonel)
go*gle # "ggle", "gogle", "google", "gooogle"... (sıfır veya daha fazla)
go+gle # "gogle", "google", "gooogle"... (bir veya daha fazla)
[0-9]{3} # Tam 3 rakam
[0-9]{3,} # 3 veya daha fazla rakam
[0-9]{2,4} # 2 ila 4 rakam
[0-9]{,3} # 0 ila 3 rakam (nadir).* # Açgözlü — MÜMKÜN OLDUĞUNCA FAZLA eşleştirir
.*? # Tembel — MÜMKÜN OLDUĞUNCA AZ eşleştirir
Metin: <h1>Başlık</h1><p>Gövde</p>
<.*> # Tüm stringi eşleştirir (açgözlü)
<.*?> # "<h1>", "</h1>", "<p>", "</p>" ayrı ayrı (tembel)
(abc)+ # "abcabcabc" — grubu miktarla
(https?://)? # Opsiyonel protokol grubu
# Geri referanslar
(["']).*\1 # "merhaba" veya 'dünya' ile eşleşir (aynı tırnak)
# Adlandırılmış gruplar (PCRE)
(?<adi>kalip) # .NET, Perl 5.10+
# Yakalama yapmayan gruplar
(?:kalip) # Gruplar ama yakalamazİleriye Bakma (Lookahead) / Geriye Bakma (Lookbehind)
foo(?=bar) # Olumlu ileri bakış: "foo" sadece arkasından "bar" geliyorsa
foo(?!bar) # Olumsuz ileri bakış: "foo" sadece arkasından "bar" gelmiyorsa
(?<=foo)bar # Olumlu geri bakış: "bar" sadece öncesinde "foo" varsa
(?<!foo)bar # Olumsuz geri bakış: "bar" sadece öncesinde "foo" yoksaÖnemli:
grep -PGNU grep'te PCRE'yi etkinleştirir. macOSgrepBSD'dir — PCRE içinggrepveyapcre2grepkullanın.
# Temel kullanım
grep "kalıp" dosya.txt
grep "hata" /var/log/syslog
# Yaygın seçenekler
grep -i "uyari" log.txt # Büyük/küçük harf duyarsız
grep -r "TODO" src/ # Özyinelemeli
grep -l "fonksiyon" *.sh # Sadece dosya adlarını listele
grep -c "hata" log.txt # Eşleşme sayısı
grep -n "kalıp" dosya.txt # Satır numaralarını göster
grep -v "hariç" dosya.txt # Ters eşleştir
grep -w "tam" dosya.txt # Tam kelime eşleşmesi
grep -x "tam satır" dosya.txt # Tam satır eşleşmesi
grep -A 3 "kalıp" dosya.txt # Eşleşmeden SONRA 3 satır
grep -B 2 "kalıp" dosya.txt # Eşleşmeden ÖNCE 2 satır
grep -C 5 "kalıp" dosya.txt # Eşleşme etrafında 5 satır
# Genişletilmiş regex (ERE) — -E kullan
grep -E "^[0-9]{3}-[0-9]{4}$" dosya.txt
# Perl uyumlu regex (PCRE)
grep -P "\d{3}-\d{4}" dosya.txt # BSD grep'te çalışmaz
# Pratik örnekler
grep -E "(HATA|KRITIK)" uygulama.log
grep -oE "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" erisim.log | sort | uniq -c | sort -rn
grep -E "^[^#]" /etc/ssh/sshd_config # Yorum olmayan satırlar
grep -c "POST /api" erisim.log # API çağrılarını say# Değiştirme
sed 's/eski/yeni/' dosya.txt # Satırdaki ilk eşleşme
sed 's/eski/yeni/g' dosya.txt # Tüm eşleşmeler (global)
sed 's/eski/yeni/2' dosya.txt # Sadece ikinci eşleşme
sed 's/eski/yeni/gi' dosya.txt # Global + büyük/küçük harf duyarsız
sed 's/\(parca1\)\(parca2\)/\2\1/' dosya.txt # Geri referans
# Yerinde düzenleme
sed -i 's/eski/yeni/g' dosya.txt # GNU sed
sed -i '' 's/eski/yeni/g' dosya.txt # BSD sed (macOS)
# Satır adresleme
sed '3s/foo/bar/' dosya.txt # Sadece 3. satır
sed '5,10s/foo/bar/' dosya.txt # 5–10. satırlar
sed '/hata/s/foo/bar/' dosya.txt # "hata" ile eşleşen satırlar
sed '/^$/d' dosya.txt # Boş satırları sil
sed -n '10,20p' dosya.txt # 10–20. satırları yazdır
# Çoklu komut
sed -e 's/foo/bar/' -e 's/baz/qux/' dosya.txt
sed 's/foo/bar/; s/baz/qux/' dosya.txt# Alan çıkarma
awk '{print $1, $NF}' dosya.txt # İlk ve son alan
awk -F: '{print $1, $3}' /etc/passwd # Özel alan ayracı
# Kalıp eşleştirme
awk '/hata/ {print NR, $0}' log.txt
awk '$3 > 100 {print $1, $3}' veri.txt
# Yerleşik değişkenler
awk '{print NR, NF, $0}' dosya.txt # Satır no, alan sayısı, tüm satır
awk 'NR > 1 {print}' dosya.txt # Başlık satırını atla
# BEGIN/END blokları
awk 'BEGIN {toplam=0} {toplam+=$1} END {print "Toplam:", toplam}' sayilar.txt
# Formatlı çıktı
awk '{printf "%-20s %5d\n", $1, $2}' dosya.txt
# Çoklu koşul
awk '$3 >= 90 && $3 <= 100 {print}' notlar.txt# Satır satır (güvenilir — boşlukları korur, tüm karakterleri işler)
while IFS= read -r satir; do
echo "$satir"
done < "dosya.txt"
# Dosya tanıtıcısı ile (alt kabuk sorunlarını önler)
while IFS= read -r -u3 satir; do
echo "$satir"
done 3< "dosya.txt"
# Tüm dosyayı değişkene oku
icerik=$(<dosya.txt)
# Belirli satırları oku
mapfile -t satirlar < dosya.txt # Tüm satırları diziye oku (bash 4+)
satir=$(sed -n '5p' dosya.txt) # 5. satırecho "metin" > dosya.txt # Üzerine yaz (truncate)
echo "metin" >> dosya.txt # Ekle
cat > dosya.txt << EOF
Çok satırlı
içerik
EOF
cat >> dosya.txt << EOF
Eklenen içerik
EOF
# Girintili heredoc (sekmeleri temizle)
cat >> dosya.txt <<- EOF
Girintili içerik
EOF
# Değişken genişletmeyi engelleyen heredoc
cat >> dosya.txt << 'EOF'
$HOME burada genişlemez
EOFexec 3< girdi.txt # FD 3'te okuma için aç
exec 4> cikti.txt # FD 4'te yazma için aç
exec 5>> ekle.txt # FD 5'te ekleme için aç
exec 6<> okumayaz.txt # FD 6'da okuma-yazma için aç
while read -u3 satir; do
echo "$satir" >&4
done
exec 3<&- # FD 3'ü kapa
exec 4>&- # FD 4'ü kapa# Dosyanın içeriği var mı?
[[ -s "$dosya" ]] && echo "İçerik var" || echo "Boş"
# Geçici dosyalar
gecici=$(mktemp /tmp/script_XXXXX)
trap 'rm -f "$gecici"' EXIT
# Geçici dizin
gecici_dizin=$(mktemp -d /tmp/script_dizini_XXXXX)
# Kilit dosyası
kilit="/var/run/script.lock"
exec 200>"$kilit"
flock -n 200 || { echo "Script zaten çalışıyor"; exit 1; }komut > dosya # stdout → dosya
komut 2> dosya # stderr → dosya
komut &> dosya # stdout ve stderr → dosya
komut > dosya 2>&1 # Klasik: stdout sonra stderr → dosya
komut >> dosya 2>&1 # İkisini de ekle
komut < dosya # stdin dosyadan
komut <<< "string" # stdin string'den (here-string)
komut > /dev/null # stdout'u yok say
komut 2>&- # stderr'i kapa
# Grup çıktısı
{
komut1
komut2
} > dosya
# Süreç ikamesi (process substitution)
diff <(ls dizin1) <(ls dizin2)
while read -r satir; do ... done < <(komut)ESIK=80
df -h | awk 'NR>1 {gsub(/%/,"",$5); if($5 > ESIK) print $0}'# Log'ları arşivle ve sıfırla
log_dosyasi="/var/log/uygulama.log"
max_boyut=$(( 100 * 1024 * 1024 )) # 100MB
if [[ -f "$log_dosyasi" ]] && [[ $(stat -f%z "$log_dosyasi") -gt $max_boyut ]]; then
gzip -c "$log_dosyasi" > "${log_dosyasi}.$(date +%Y%m%d-%H%M%S).gz"
: > "$log_dosyasi" # Sıfırla (silme, truncate)
fiyedek_dizinleri=("/etc" "/home" "/var/www")
yedek_tabani="/yedek/$(date +%Y/%m)"
mkdir -p "$yedek_tabani"
for dizin in "${yedek_dizinleri[@]}"; do
yedek_adi="$(basename "$dizin")_$(date +%Y%m%d_%H%M%S).tar.gz"
tar -czf "$yedek_tabani/$yedek_adi" "$dizin"
done# CPU Yükü
cpu_bos=$(top -bn1 | grep "Cpu(s)" | awk '{print $8}' | cut -d'.' -f1)
[[ $cpu_bos -lt 20 ]] && echo "KRITIK: CPU boşluk %20'nin altında"
# Bellek Kullanımı
bellek_bos=$(free -m | awk '/^Mem:/ {print $4}')
[[ $bellek_bos -lt 500 ]] && echo "KRITIK: Boş bellek 500MB altında"
# Disk I/O
iostat -x 1 2 | tail -n +4 | awk '$1 ~ /^[a-z]/ {if ($14 > 90) print $1 " kullanım > %90"}'
# Servis Durumu
servis_kontrol() {
systemctl is-active --quiet "$1" || echo "Servis $1 çalışmıyor"
}#!/bin/bash
set -euo pipefail
konteyner_adi="$1"
zaman_asimi="${2:-30}"
for (( i=0; i<zaman_asimi; i++ )); do
if docker inspect --format '{{.State.Health.Status}}' "$konteyner_adi" 2>/dev/null | grep -q "healthy"; then
echo "Konteyner $konteyner_adi sağlıklı"
exit 0
fi
sleep 1
done
echo "Zaman aşımı: Konteyner $konteyner_adi ${zaman_asimi}s içinde sağlıklı olmadı"
exit 1AI ajanları yapılandırılmış veri tüketir. Script'leri güvenilir JSON ayrıştırması için tasarlayın:
#!/bin/bash
set -euo pipefail
rapor_olustur() {
local sunucu_adi="$(hostname)"
local calisma_suresi="$(uptime -p)"
local cpu_yuku="$(uptime | awk -F'load average:' '{print $2}' | tr -d ' ')"
jq -n \
--arg sunucu "$sunucu_adi" \
--arg sure "$calisma_suresi" \
--arg cpu "$cpu_yuku" \
'{hostname: $sunucu, uptime: $sure, cpu_load: $cpu}'
}
rapor_olusturAI ajanları işlemleri tekrarlayabilir. Script'ler idempotent olmalıdır (birden çok çalıştırmada güvenli):
kullanici_olustur() {
local kullanici_adi="$1"
if id "$kullanici_adi" &>/dev/null; then
echo "Kullanıcı $kullanici_adi zaten var — atlanıyor"
return 0
fi
useradd -m -s /bin/bash "$kullanici_adi"
echo "Kullanıcı $kullanici_adi oluşturuldu"
}AI ajanlarının script'lerden net sinyallere ihtiyacı vardır. Her zaman anlamlı çıkış kodları döndürün:
# 0: Başarılı / İşlem gerekmez
# 1: Geçici hata (tekrar denenebilir)
# 2: Kalıcı hata (tekrar deneme)
# 3: Idempotent atlama (zaten yapılmış)
yapilandirma_guncelle() {
local dosya="$1"
if [[ ! -f "$dosya" ]]; then
return 2 # Kalıcı — yapılandırma dosyası yok
fi
# ... güncelleme mantığı ...
}AI ajanlarının sorgulayabileceği durum dosyaları sağlayın:
AI_DURUM_DIZINI="/var/lib/ai-agent"
durum_raporla() {
local gorev_id="$1"
local durum="$2"
local mesaj="$3"
mkdir -p "$AI_DURUM_DIZINI"
cat > "$AI_DURUM_DIZINI/${gorev_id}.json" << EOF
{
"task_id": "$gorev_id",
"status": "$durum",
"message": "$mesaj",
"timestamp": "$(date -Iseconds)"
}
EOF
}Dosya sistemi olaylarına yanıt veren script'ler tasarlayın:
inotifywait -m -e create -e modify /var/spool/upload/ |
while read -r dizin olaylar dosya; do
/usr/local/bin/islemeyi-baslat.sh "$dizin/$dosya"
doneAI orkestratörü, LLM'lerle yapılandırılmış JSON şeması üzerinden haberleşir:
{
"workflow": "gece_bakimi",
"trigger": "cron_schedule",
"tasks": [
{
"id": "saglik_kontrol",
"type": "health_check",
"timeout": 30,
"depends_on": [],
"on_success": "baslat_yedekleme",
"on_failure": "bildirim_gonder"
}
],
"llm_context": {
"model": "claude-3-opus",
"max_tokens": 4000,
"temperature": 0.1
}
}LLM'den gelen next_action alanı, orkestratörün bir sonraki adımını belirler. Bu sayede AI, iş akışını dinamik olarak yönlendirebilir.
# KÖTÜ — tekrarlanan mantık
echo "[$(date +'%Y-%m-%d %H:%M:%S')] İşlem A başlatılıyor" >> /var/log/uygulama.log
./islem_a.sh
echo "[$(date +'%Y-%m-%d %H:%M:%S')] İşlem B başlatılıyor" >> /var/log/uygulama.log
./islem_b.sh
# İYİ — fonksiyon çıkarma
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" >> /var/log/uygulama.log
}
log "İşlem A başlatılıyor"
./islem_a.sh
log "İşlem B başlatılıyor"
./islem_b.shGirdileri ve ön koşulları işlemin başında doğrulayın, yarısında değil:
#!/bin/bash
set -euo pipefail
# Önce TÜM girdileri doğrula
[[ $# -ge 2 ]] || { echo "Kullanım: $0 <kullanici> <rol>"; exit 1; }
[[ -f "/etc/yapilandirma.json" ]] || { echo "Yapılandırma bulunamadı"; exit 1; }
command -v jq &>/dev/null || { echo "jq gerekli"; exit 1; }
[[ $EUID -eq 0 ]] || { echo "Root yetkisi gerekli"; exit 1; }
# Şimdi güvenle çalıştırBir script'in birden çok çalıştırılması, tek çalıştırılmasıyla aynı sonucu vermelidir:
# Idempotent: mkdir -p zaten idempotent
mkdir -p /yedek/gunluk
# Idempotent: Sil ve yeniden oluştur (dikkatli!)
rm -rf /tmp/calisma_dizini && mkdir -p /tmp/calisma_dizini
# Idempotent değil: Her çalıştırmada yeni satır ekler (KÖTÜ)
echo "kayit" >> /etc/hosts
# Idempotent: Sadece yoksa ekle (İYİ)
grep -q "kayit" /etc/hosts || echo "kayit" >> /etc/hostsTek bir işi iyi yapan küçük, birleştirilebilir fonksiyonlar:
# Tek sorumluluklu fonksiyonlar
ip_dogrula() { ... }
port_kontrol() { ... }
uyari_gonder() { ... }
main() {
local ip="$1"
ip_dogrula "$ip" || exit 1
port_kontrol "$ip" 80 || uyari_gonder "Port 80 kapalı: $ip"
}ilerleme_goster() {
local mevcut="$1"
local toplam="$2"
local yuzde=$(( mevcut * 100 / toplam ))
printf "\rİlerleme: [%-50s] %d%%" "$(printf '#%.0s' $(seq 1 $((yuzde/2))))" "$yuzde"
}#!/usr/bin/env bash
#
# Ad: deploy.sh
# Açıklama: Kesintisiz dağıtım otomasyonu
# Kullanım: ./deploy.sh <ortam> <surum>
# ortam: staging | production
# surum: Semantic version (örn. 2.1.4)
# Ön Koşullar:
# - jq kurulu (brew install jq)
# - SSH anahtarı hedef sunuculara yüklenmiş
# - Docker registry kimlik doğrulaması
# Çıkış Kodları:
# 0 — Dağıtım başarılı
# 1 — Geçersiz argümanlar
# 2 — Ön kontrol başarısız
# 3 — Dağıtım başarısız (geri alındı)# config.sh (çalıştırılmaz, kaynak olarak yüklenir)
readonly APP_HOME="/opt/uygulamam"
readonly LOG_DIR="/var/log/uygulamam"
readonly YEDEK_DIR="/yedek/uygulamam"
readonly MAX_LOG_YAS=7 # Gün
readonly ALARM_EMAIL="ops@ornek.com"
readonly SLACK_WEBHOOK="https://hooks.slack.com/services/..."
# Yükle:
[[ -f "./config.sh" ]] && source "./config.sh"
[[ -f "/etc/uygulamam/config.sh" ]] && source "/etc/uygulamam/config.sh"# Özellik tespiti, varsayım değil
if command -v jq &>/dev/null; then
cikti_formatla() { jq -r "$@"; }
else
cikti_formatla() { echo "jq kurulu değil — ham çıktı"; cat; }
fideclare -A LOG_SEVIYELERI=([DEBUG]=0 [BILGI]=1 [UYARI]=2 [HATA]=3)
LOG_SEVIYESI="${LOG_SEVIYESI:-BILGI}"
log() {
local seviye="$1"
shift
[[ ${LOG_SEVIYELERI[$seviye]} -ge ${LOG_SEVIYELERI[$LOG_SEVIYESI]} ]] || return 0
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')] [$seviye] $*" >&2
}readonly KIRMIZI='\033[0;31m'
readonly YESIL='\033[0;32m'
readonly SARI='\033[1;33m'
readonly MAVI='\033[0;34m'
readonly NC='\033[0m' # Renk Yok
renklendir() {
local renk="$1"
shift
echo -e "${renk}$*${NC}"
}
basarili() { renklendir "$YESIL" "[✓] $*"; }
hata() { renklendir "$KIRMIZI" "[✗] $*" >&2; }
uyari() { renklendir "$SARI" "[!] $*"; }
bilgi() { renklendir "$MAVI" "[i] $*"; }| Kaynak | Konular |
|---|---|
| KodeKloud — Linux'ta Düzenli İfadeler | Regex meta-karakterleri, grep, sed, awk, log analizi |
| TechWorld with Nana — Bash Scripting Eğitimi | Bash temelleri, otomasyon, DevOps script'leri |
- GNU Bash Manual — Kesin referans
- Bash Hackers Wiki — Topluluk odaklı en iyi uygulamalar
- Google Shell Stil Rehberi — Endüstri standartları
- Regular-Expressions.info — Eğitimler ve referanslar
- Regex101 — Etkileşimli regex test aracı
Mühendislik Verimliliği · Sürdürülebilir Tasarım · Üretime Hazır Otomasyon
github.com/BerattCelikk/DevOps-Bash-RegEx-Mastery