- Bash Script Nedir ve Linux Otomasyonu Nasıl Kurulur?
- Bir Bash script hangi parçalarla çalışır?
- Değişkenler ve komut çıktıları
- Koşullar ve çıkış kodları
- Fonksiyonlar, döngüler ve tekrar eden işler
- Gerçek bir otomasyon örneği: disk alarmı
- Cron ile zamanlanmış Bash görevleri
- Girdi doğrulama ve güvenli Bash alışkanlıkları
- Bash script ile Python veya Ansible ne zaman ayrılır?
- Script'i test etmek ve izlemek
- Sık Sorulan Sorular
Bash Script Nedir ve Linux Otomasyonu Nasıl Kurulur?
Gece vardiyasında aynı komutu üçüncü kez yazarken sorun çoğu zaman komutta değildir. Sorun, o işi hâlâ elle yapıyor olmamızdır. Birkaç dosyayı kontrol etmek, disk kullanımını ölçmek, eski logları arşivlemek veya servisleri belirli bir sırayla yeniden başlatmak kısa vadede kolay görünür. Bir süre sonra vakit kaybettirir ve insan hatasına açık hâle gelir.
Bash script nedir? Bash tarafından çalıştırılan; komutları, değişkenleri ve kontrol yapılarını tek bir dosyada birleştiren otomasyon betiğidir. Linux sunucuda elle yaptığınız adımları sıraya koyabilir, koşullara bağlayabilir, çıktı üretebilir ve cron gibi zamanlayıcılarla çalıştırabilirsiniz.
Ben Bash script’i küçük sistem yönetimi işleri için hâlâ pratik buluyorum. Her iş için YAML dosyası hazırlamak gerekmiyor. Fakat iki kez tekrarladığım bir işi üçüncü kez elle yapmadan önce kendime şunu soruyorum: “Bunu yarın başka biri çalıştırsa ne olur?” Cevap belirsizse script’i yazıp güvenli hâle getiriyorum.
Bir Bash script hangi parçalarla çalışır?
Bash script dosyaları çoğunlukla .sh uzantısıyla kaydedilir. Uzantı zorunlu değildir; Linux için dosyanın içeriği ve çalıştırma biçimi daha önemlidir. Yine de .sh, dosyanın bir shell betiği olduğunu hızlıca anlatır.
Dosyanın en üstünde kullanılacak yorumlayıcıyı belirtmek iyi bir alışkanlıktır:
#!/usr/bin/env bash
printf 'Merhaba, %sn' "$USER"
İlk satıra shebang denir. /usr/bin/env bash ifadesi Bash’i sistemin PATH değişkeninde arar. Bazı dağıtımlarda Bash yolu doğrudan /bin/bash olabilir. env kullanmak farklı sistemler arasında taşınabilirliği bir miktar artırır.
printf kullandım çünkü biçimlendirme davranışı echo‘ya göre daha öngörülebilirdir. Basit bir mesaj için ikisi de iş görür. Script büyüdükçe küçük belirsizliklerin birikmesini istemem.
İlk script’i oluşturma ve çalıştırma
Önce dosyayı açın:
vim disk-kontrol.sh
İçine şu satırları ekleyebilirsiniz:
#!/usr/bin/env bash
set -u
printf 'Hostname: %sn' "$(hostname)"
printf 'Tarih: %sn' "$(date '+%Y-%m-%d %H:%M:%S')"
printf 'Disk kullanımı:n'
df -h /
set -u, tanımlanmamış bir değişken kullanıldığında script’in hata vermesini sağlar. Tek başına bütün hataları yakalamaz; yine de yazım hatalarını erkenden görmeme yardımcı olur.
Dosyayı çalıştırılabilir yapmak için:
chmod +x disk-kontrol.sh
./disk-kontrol.sh
Alternatif olarak izin eklemeden doğrudan Bash’e verebilirsiniz:
bash disk-kontrol.sh
İki yöntem arasındaki fark önemlidir. ./disk-kontrol.sh dosyanın çalıştırma iznine ve doğru shebang satırına güvenir. bash disk-kontrol.sh ise dosyayı özellikle Bash ile çalıştırır. Bash’e özgü diziler veya [[ ... ]] kullanıyorsanız sh ile çalıştırmayın; sh script.sh bazı sistemlerde farklı bir shell çağırabilir.
Değişkenler ve komut çıktıları
Script’lerin kullanışlı hâle gelmesi, sabit metinleri değişkenlerden ayırınca başlar. Değişken atarken eşittir işaretinin çevresinde boşluk bırakılmaz:
#!/usr/bin/env bash
BACKUP_DIR="/var/backups"
HOST="$(hostname -s)"
NOW="$(date '+%Y%m%d-%H%M%S')"
printf 'Sunucu: %sn' "$HOST"
printf 'Yedek dizini: %sn' "$BACKUP_DIR"
printf 'Zaman etiketi: %sn' "$NOW"
Değişkenleri kullanırken çift tırnakları ihmal etmeyin. Özellikle dosya yollarında boşluk bulunabiliyorsa "$BACKUP_DIR" ile $BACKUP_DIR aynı davranmayabilir. Sunucularımda boşluk içeren yolları tercih etmiyorum ama script’i güvenli yazmak için bu alışkanlığı koruyorum.
Komut çıktısını değişkene almak için $(...) kullanılır. Eski backtick biçimi de çalışır; iç içe komutlarda okunması daha zordur.
Argüman almak
Bir script’in her seferinde dosyanın içindeki yolu değiştirmenizi istemesi pratik değildir. Komut satırı argümanları burada devreye girer:
#!/usr/bin/env bash
if [[ $# -ne 1 ]]; then
printf 'Kullanım: %s DİZİNn' "$0" >&2
exit 1
fi
TARGET="$1"
if [[ ! -d "$TARGET" ]]; then
printf 'Hata: dizin bulunamadı: %sn' "$TARGET" >&2
exit 2
fi
du -sh -- "$TARGET"
$# verilen argüman sayısını, $1 ilk argümanı, $0 ise script’in adını temsil eder. Buradaki --, yolun tireyle başlaması gibi durumlarda komutun bunu seçenek sanmasını engeller. Küçük bir ayrıntı. Dosya işlemlerinde küçük ayrıntılar genellikle gece vardiyasının büyük ayrıntılarına dönüşür.
Koşullar ve çıkış kodları
Otomasyonun güvenilir olması için komutları art arda çalıştırmak yetmez. Bir adım başarısız olduğunda sonraki adımın çalışıp çalışmayacağına karar vermek gerekir.
#!/usr/bin/env bash
SERVICE="nginx"
if systemctl is-active --quiet "$SERVICE"; then
printf '%s çalışıyor.n' "$SERVICE"
else
printf '%s çalışmıyor.n' "$SERVICE" >&2
exit 1
fi
systemctl is-active --quiet ekrana gereksiz çıktı basmadan yalnızca çıkış kodu döndürür. Shell’de 0 başarı, sıfır dışındaki değerler hata kabul edilir. Yeni başlayanların ters bulduğu bir ayrıntıdır: Bash’te doğru kabul edilen durum 0‘dır.
Dosya kontrollerinde yaygın test operatörlerinden bazıları şöyledir:
| Test | Anlamı |
|---|---|
-f |
Normal dosya var mı? |
-d |
Dizin var mı? |
-r |
Okunabilir mi? |
-w |
Yazılabilir mi? |
-s |
Dosya boş değil mi? |
Birden fazla komutun başarısızlığında script’i durdurmak için şu başlangıç satırı sık kullanılır:
set -Eeuo pipefail
-e başarısız bir komutta çıkmayı, -u tanımsız değişkenleri hata saymayı, pipefail ise pipe içindeki herhangi bir komutun hatasını üst komuta taşımayı sağlar. -E, hata yakalama davranışının fonksiyonlar ve alt kabuklarda daha tutarlı olmasına yardım eder.
Bu seçenekleri körlemesine eklemek de doğru değildir. Bazı komutlar beklenen koşullarda sıfır dışı kod döndürebilir. Örneğin grep, eşleşme bulamadığında hata değil bilgi niteliğinde bir çıkış kodu döndürür. Script’in hangi adımda ne beklediğini bilerek kullanın.
Fonksiyonlar, döngüler ve tekrar eden işler
Script uzadıkça aynı kodu kopyalamak yerine fonksiyon kullanırım:
log() {
printf '[%s] %sn' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
}
fail() {
log "HATA: $*" >&2
exit 1
}
log 'Kontrol başlıyor'
[[ -d /var/log ]] || fail '/var/log bulunamadı'
log 'Kontrol tamamlandı'
log fonksiyonu her mesaja zaman bilgisi ekliyor. Bir cron işinin ne zaman çalıştığını araştırırken bu zaman damgası değerlidir. Çıktıyı sonradan journalctl veya bir dosya üzerinden takip edebilirsiniz.
Döngüler, birden fazla dosya ya da sunucu üzerinde aynı işlemi yapmak için kullanılır:
for file in /var/log/*.log; do
[[ -e "$file" ]] || continue
printf '%s: ' "$file"
wc -l < "$file"
done
Buradaki [[ -e "$file" ]] kontrolü, dizinde eşleşen dosya yoksa glob ifadesinin düz metin olarak kalmasını engeller. Bu ayrıntıyı atladığım eski bir script, olmayan /var/log/*.log dosyasını işlemeye çalışmıştı. Büyük bir arıza değildi ama gereksiz alarm üretti. İzleme sisteminde gürültü de bir arıza türüdür.
Gerçek bir otomasyon örneği: disk alarmı
Sunucuda disk alanı azaldığında yalnızca alarm göndermek yerine önce hangi dosya sisteminin dolduğunu, sonra da büyük dizinleri göstermek daha faydalıdır. Aşağıdaki örnek basit bir kontrol için yeterlidir:
#!/usr/bin/env bash
set -Eeuo pipefail
THRESHOLD=80
MOUNTPOINT="/"
usage=$(df -P "$MOUNTPOINT" | awk 'NR==2 {gsub("%", "", $5); print $5}')
if (( usage >= THRESHOLD )); then
printf 'UYARI: %s kullanımı %s%% seviyesinde.n' "$MOUNTPOINT" "$usage" >&2
printf 'En büyük dizinler:n' >&2
du -xhd1 "$MOUNTPOINT" 2>/dev/null | sort -h | tail -n 8 >&2
exit 1
fi
printf 'Disk normal: %s kullanım.n' "$usage"
df -P, çıktıyı daha düzenli ve script’ler için daha öngörülebilir hâle getirir. awk yüzde işaretini kaldırıp yalnızca sayıyı alıyor. du -x ise başka dosya sistemlerine geçmeden mevcut mount üzerinde kalıyor; bağlı bir uzak dosya sistemi yüzünden kontrolün beklenmedik biçimde uzamasını önlüyor.
Bir keresinde gece üçte /var bölümü dolmaya yaklaşmıştı. İlk refleksim servisi yeniden başlatmak oldu (evet, ben de o refleksi yıllarca törpüledim). Sonra ncdu ile baktığımda uygulamanın log rotation yapılandırmasının devre dışı kaldığını gördüm. Sorunu yeniden başlatmayla örtmek yerine logrotate kuralını düzelttim. Bash script burada tek başına çözüm değildi; doğru teşhisi tekrarlanabilir bir kontrole dönüştürmemi sağladı.
Cron ile zamanlanmış Bash görevleri
Bir script’i elle çalıştırmak başlangıç için yeterlidir. Düzenli kontrol veya yedekleme gerekiyorsa cron kullanılabilir. Önce script’i elle, mümkünse cron’un kullanacağı kullanıcıyla test edin:
sudo -u backup /usr/local/sbin/disk-kontrol.sh
Ardından ilgili kullanıcının crontab’ını açın:
crontab -e
Her 15 dakikada çalışacak bir satır şöyle görünür:
*/15 * * * * /usr/local/sbin/disk-kontrol.sh >> /var/log/disk-kontrol.log 2>&1
Cron ortamı etkileşimli shell ortamından daha sınırlıdır. PATH farklı olabilir, çalışma dizini beklediğiniz yer olmayabilir ve bazı ortam değişkenleri hiç bulunmayabilir. Kritik komutlarda mutlak yollar kullanmak veya script’in başında makul bir PATH tanımlamak bu yüzden iyi fikirdir.
PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
export PATH
Cron konusunda başımdan geçen derslerden biri, yanlış sunucuda yaptığım küçük bir zamanlama değişikliğinin etkisini fark etmemle ilgiliydi. Bir production shell’inde cron satırını düzenlemeye başlamıştım; tab tamamlamanın gösterdiği hostname, staging sandığım makinenin production olduğunu ortaya çıkardı. O günden beri komut çalıştırmadan önce hostname kontrol ediyor, kritik oturumlarda kırmızı prompt kullanıyorum. Otuz saniye sürüyor.
Çakışan çalışmaları engellemek
Yedekleme veya uzun süren rapor script’lerinde flock kullanabilirsiniz:
#!/usr/bin/env bash
set -Eeuo pipefail
exec 9>/run/lock/my-backup.lock
flock -n 9 || {
printf 'Başka bir yedekleme zaten çalışıyor.n' >&2
exit 1
}
printf 'Yedekleme başladı.n'
Uzun süren yedekleme komutları burada çalışır.
Script’in başında açılan dosya tanıtıcısı üzerinden kilit alınır. Script kapanınca kilit serbest kalır. Bu yaklaşım, aynı cron işinin iki kez çalışıp aynı hedefe eşzamanlı yazmasını önler.
Girdi doğrulama ve güvenli Bash alışkanlıkları
Bir Bash script’i root yetkisiyle çalışıyorsa kullanıcı girdisini doğrudan komuta eklemek risklidir. Özellikle eval kullanımı ve kullanıcıdan gelen metni shell kodu gibi çalıştırmak tehlikelidir.
Dosya yolu kabul eden basit bir script’te en azından şu kontrolleri yapın:
- Beklenen argüman sayısını doğrulayın.
- Dosya veya dizinin gerçekten var olduğunu kontrol edin.
- Silme işlemlerinde hedef yolun boş olup olmadığına ayrıca bakın.
- Değişkenleri çift tırnak içinde kullanın.
- Silme veya taşıma öncesinde önce kuru çalıştırma seçeneği sunun.
- Gerekmedikçe root kullanmayın.
Şu yaklaşım özellikle tehlikelidir:
rm -rf "$TARGET"/*
TARGET boş veya yanlış bir değere dönüşürse sonuç tahmin ettiğiniz gibi olmayabilir. Ben silme script’lerinde hedefi izin verilen üst dizinlerle karşılaştırmadan işlem yaptırmam:
if [[ -z "${TARGET:-}" || "$TARGET" == "/" ]]; then
printf 'Güvenli olmayan hedef, işlem iptal edildi.n' >&2
exit 1
fi
Bu kontrol tek başına kusursuz güvenlik sağlamaz. Yanlış değişken, eksik argüman ve aceleyle yapılan çalıştırmaların bir bölümünü yakalar. Üretim sunucusunda silme yapmadan önce hostname kontrol ederim. Tab tamamlamanın bana production hostname’ini göstermesi, yanlış sunucuda işlem yapmamı son saniyede engellemişti.
Bash script ile Python veya Ansible ne zaman ayrılır?
Bash her işi çözmez. Tek bir Linux sunucuda birkaç komutu birleştirmek, log ve dosya işlemleri yapmak, servis durumunu kontrol etmek için çok uygundur. Veri yapıları büyüdüğünde, karmaşık hata yönetimi gerektiğinde veya HTTP API’leri yoğun biçimde kullanıldığında Python daha okunaklı olabilir.
Birden fazla sunucunun kurulumunu aynı biçimde yapmak istiyorsanız Ansible daha doğru bir katman sunar. Benim ölçümüm basit: Elle iki kez yaptığım işi üçüncüde playbook’a taşırım. Tek satırlık bir paket kurulumunu sırf otomasyon adı geçsin diye YAML’a çevirmem.
| İhtiyaç | Daha uygun seçenek |
|---|---|
| Tek sunucuda dosya ve servis işlemleri | Bash |
| Karmaşık veri işleme ve API entegrasyonu | Python |
| Birden fazla sunucuda tekrarlanabilir yapılandırma | Ansible |
| Dağıtım hattında küçük yardımcı görev | Bash veya pipeline aracı |
Linux sunucuyu ilk kurulumdan sonra hazırlamak için Yeni VPS Sunucuyu 30 Dakikada Canliya Alma Rehberi içindeki adımları bir Bash script’ine aktarmak cazip gelebilir. SSH anahtarı, firewall, kullanıcı ve paket kurulumu gibi adımlarda tekrar çalıştırılabilirlik tasarlanmadan yazılan script’ler ikinci çalıştırmada mevcut ayarları bozabilir. Her komutun zaten hazırlanmış bir sistemde nasıl davranacağını önce test edin.
Cloud-init de ilk açılış yapılandırmalarında Bash komutları çalıştırabilir. Bu noktada Cloud-Init Nedir? VPS Kurulumunu Otomatikleştirme içeriğindeki yaklaşım işinize yarayabilir. Bash betiğini doğrudan cloud-init içine gömmek yerine ayrı bir dosyaya koymak, sürümlemek ve loglamak genellikle daha temizdir.
Script’i test etmek ve izlemek
Bir script’in çalışması, doğru çalıştığı anlamına gelmez. Önce ayrı bir test VPS’inde veya benim yaptığım gibi Proxmox laboratuvarındaki geçici bir sanal makinede deneyin. Müşteri sunucusu deneme alanı değildir.
Debug için Bash’in izleme modunu kullanabilirsiniz:
bash -x ./disk-kontrol.sh
Bu komut çalıştırılan satırları gösterir. Hassas parolalar veya token’lar komut satırında bulunuyorsa çıktıyı paylaşmadan önce temizleyin. Gerekirse yalnızca belirli bölümü izlemek için set -x ve set +x kullanabilirsiniz.
Script’lerin çıktısını nereye yazdığınızı bilin. Cron altında standart çıktı çoğu sistemde e-posta olarak gönderilebilir; yanlış yapılandırmada posta kuyruğu büyüyebilir. Kritik script’lerde ya açıkça bir log dosyasına yönlendirme yaparım ya da systemd timer ve journal kullanırım.
Bir script için şu soruların cevabı net değilse henüz hazır değildir:
- Başarılı çalıştığını nasıl anlayacağım?
- Bir adım hata verirse sonraki adımlar çalışmalı mı?
- Aynı anda iki kopya çalışırsa ne olur?
- Log nerede tutulacak ve ne kadar saklanacak?
- Geri alma veya yeniden çalıştırma davranışı nedir?
Basit görünür. Sunucuda beş yıl boyunca çalışan bir cron işinin sahibi olduğunuzda bu soruların her biri gerçek bir bakım işine dönüşür. Script’i bugün yazarken yarınki kendinize açıklama bırakın: değişken adlarını açık seçin, kritik adımlara yorum ekleyin ve önce staging ortamında çalıştırın.
Linux’ta script çalıştırırken kullanılan sudo ve diğer yönetim izinlerinin ne anlama geldiğini daha iyi kavramak için [admin ne demek] rehberime de göz atabilirsiniz; burada admin hesabının görevlerini ve yetkilerini sade bir dille açıklıyorum.
Sık Sorulan Sorular
Bash script ne için kullanılır?
Linux’ta komutları sıraya koymak, dosya işlemlerini otomatikleştirmek, servis kontrolü yapmak, yedekleme ve bakım görevlerini zamanlamak için kullanılır. Küçük ve orta ölçekli sistem yönetimi işlerinde hızlı bir çözüm sunar.
Bash script nasıl çalıştırılır?
Dosyaya çalıştırma izni verip ./script.sh komutunu kullanabilir veya doğrudan bash script.sh yazabilirsiniz. Dosyada Bash’e özgü söz dizimi varsa sh script.sh yerine Bash kullanmanız gerekir.
Bash script root olarak çalıştırılmalı mı?
Gerekmedikçe hayır. Yalnızca ihtiyaç duyulan komutlara sınırlı yetki vermek, script’teki bir hatanın etkisini azaltır. Root gerekiyorsa bile girdileri doğrulamak ve silme işlemlerine koruma eklemek gerekir.
Bash yerine Python veya Ansible ne zaman seçilmeli?
Karmaşık veri işleme ve API kullanımı arttığında Python, çok sayıda sunucuda tekrarlanabilir yapılandırma gerektiğinde Ansible daha uygun olabilir. Tek sunucuda birkaç güvenilir komutu çalıştırmak için Bash hâlâ daha az yükle iş görür.
Türkçe
English
فارسی
Русский