Betikleri Profilleme ve Gereksiz Alt Kabuklardan Kaçınma
Betiklerin çalışma süresini ölçün ve cat-grep zincirleri gibi çok sayıda çatallanma oluşturan kalıpları yerleşik alternatiflerle değiştirin.
Betikleri Profilleme ve Gereksiz Alt Kabuklardan Kaçınma, CoddyKit'te ücretsiz bir Linux Command Line & Bash Scripting Mastery dersidir. Bu, 4 dersinin 1. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, Linux Command Line & Bash Scripting Mastery öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. Linux Command Line & Bash Scripting Mastery kursu toplamda 4 dersten oluşur.
Betiğin Performansı Neden Önemlidir
Yavaş çalışan Bash betikleri CI süresini boşa harcar, cron işlerini engeller ve kullanıcıları zor durumda bırakır. Yavaşlığın çoğu karmaşık mantıktan kaynaklanmaz; bunun yerine gereksiz işlem çatallarından kaynaklanır: çağırdığınız her harici komut yeni bir alt işlem başlatır.
Bu derste şunları öğreneceksiniz:
timevebash -xile zamanın gerçekte nereye harcandığını ölçmeyi- cat'in gereksiz kullanımı gibi çok sayıda çatal oluşturan karşıt kalıpları belirlemeyi
- Harici komutları daha hızlı kabuk yerleşikleriyle değiştirmeyi
- Alt kabukları bilinçli kullanmayı ve değer katmadıklarında bunlardan kaçınmayı
Amaç, aynı işi daha az alt işlemle ve daha kısa gerçek süreyle yapan betikler yazmaktır.
time Yerleşiğiyle Bir Betiğin Süresini Ölçme
En basit profilleme aracı, kabuğun yerleşik time komutudur. Üç ölçüm almak için herhangi bir komutun veya işlem hattının başına ekleyin:
- real — gerçek geçen süre (gerçekte beklediğiniz süre)
- user — kullanıcı alanı kodunda harcanan CPU süresi
- sys — çekirdekte harcanan CPU süresi (sistem çağrıları, G/Ç)
real ile user+sys arasında büyük bir fark olması genellikle betiğin G/Ç beklediği veya çok sayıda alt işlem başlattığı anlamına gelir. Herhangi bir şeyi iyileştirmeden önce bir sorun olduğunu doğrulamak için ilk olarak tüm betiğin etrafında time çalıştırın.
#!/usr/bin/env bash
# Time a whole script block
time {
for i in $(seq 1 1000); do
echo "line $i"
done | grep -c "5"
}
# Output example:
# 271
# real 0m0.045s
# user 0m0.038s
# sys 0m0.012sbash -x ve PS4 ile Çalıştırmayı İzleme
bash -x, çalıştırmadan önce her komutu yazdırır; buna çalıştırma izleme denir. Hangi satırların en sık çalıştığını ve harici programların beklediğinizden daha fazla çağrılıp çağrılmadığını gösterir.
Varsayılan olarak izlenen her satırın önüne + eklenir. Zaman damgalarını içerecek şekilde PS4 kullanarak bu öneki zenginleştirebilirsiniz; böylece izleme, hafif bir profilleyiciye dönüşür:
PS4, izlenen her komuttan önce genişletilir$EPOCHREALTIME(bash 5+) veya$(date +%s%N)eklemek nanosaniye çözünürlüğü sağlar- Standart hatayı bir dosyaya yönlendirip yavaş bölümleri bulmak için sonradan işleyin
#!/usr/bin/env bash
# Run with: bash -x ./myscript.sh 2>trace.log
# Or embed tracing inside the script:
export PS4='+ [${EPOCHREALTIME}] ${BASH_SOURCE}:${LINENO}: '
set -x
slow_function() {
local result
result=$(cat /etc/hostname) # fork — slow
echo "host: $result"
}
slow_function
set +x
# trace.log now contains timestamps so you can diff
# adjacent lines to find which step took longest.Gereksiz Alt Kabuk Nedir
Alt kabuk, geçerli kabuk işleminin alt işlem kopyasıdır. Şu yollarla oluşturulur:
- Komut ikamesi:
$(command) - Parantezlerle gruplama:
( commands ) - Bir kabuk yapısına yöneltme:
cmd | while read ...
Gerçekten yalıtım veya bir işlem hattı gerektiğinde alt kabuklar gereklidir. Bunları yalnızca kabuğun kendisinin işleyebileceği bir harici programı çağırmak için kullandığınızda veya bir yerleşiği gereksiz yere ek bir çatallama katmanına sardığınızda gereksiz hâle gelirler.
Modern bir Linux sisteminde her alt kabuk çatallaması yaklaşık 1–5 ms sürer. 10.000 kez çalışan bir döngüde 1000 gereksiz alt kabuk, yalnızca ek yük olarak 1–5 saniye ekler.
Klasik Karşıt Kalıp: cat'in Gereksiz Kullanımı
cat file | grep pattern, çok sayıda çatal oluşturan en ünlü karşıt kalıptır. grep dosyayı doğrudan okuyabildiği hâlde, bir işlem hattıyla birbirine bağlanmış iki işlem (cat + grep) başlatır.
Çözüm basittir: dosya adını dosyaları anlayan komuta doğrudan aktarın. Araç dosya adlarını kabul etmiyorsa buna girdi yönlendirme denir; kabul ediyorsa yalnızca cat komutunu kullanmamak yeterlidir.
- Yavaş:
cat file | grep pattern— 2 işlem, 1 işlem hattı - Hızlı:
grep pattern file— 1 işlem, işlem hattı yok - Bu da hızlıdır:
grep pattern < file— 1 işlem, standart girdi yönlendirmesi (işlem hattı arabelleği yok)
#!/usr/bin/env bash
# Create a sample file
seq 1 10000 > /tmp/numbers.txt
# --- Slow: useless cat ---
time cat /tmp/numbers.txt | grep -c "^5"
# --- Fast: grep reads the file directly ---
time grep -c "^5" /tmp/numbers.txt
# Both print the same count; the second is measurably faster
# because it skips the cat process and the inter-process pipe.Harici Komutları Kabuk Yerleşikleriyle Değiştirme
Tek satırlık birçok dönüşümün, çatallamayı tamamen önleyen bir yerleşik karşılığı vardır. Şu yaygın değişiklikleri karşılaştırın:
- Karakter dizisi uzunluğu için
echo ${#var};echo "$var" | wc -cyerine - Büyük/küçük harf dönüşümü için
${var^^}ve${var,,};echo "$var" | tr 'a-z' 'A-Z'yerine (bash 4+) - Basit değiştirme için
${var//search/replace};echo "$var" | sed 's/search/replace/'yerine - Düzenli ifade eşleşmesi için
[[ "$var" =~ pattern ]];echo "$var" | grep -q patternyerine - İlk satırı okumak için
read -r line < file;line=$(head -n1 file)yerine
Bu yerleşiklerin hiçbiri alt işlem çatallamaz. Çağrı başına tasarruf küçük olsa da döngülerin içinde büyük ölçüde birikir.
#!/usr/bin/env bash
sentence="hello world from bash"
# --- Fork-heavy ---
upper_slow=$(echo "$sentence" | tr 'a-z' 'A-Z')
length_slow=$(echo "$sentence" | wc -c)
# --- Builtin equivalents (zero extra processes) ---
upper_fast=${sentence^^}
length_fast=${#sentence}
echo "Slow upper : $upper_slow"
echo "Fast upper : $upper_fast"
echo "Slow length: $length_slow"
echo "Fast length: $length_fast"Döngülerin İçinde Alt Kabuklardan Kaçınma
Bir döngünün içindeki komut ikamesi, çatallama maliyetini yineleme sayısıyla çarpar. $(date) çağrısı içeren ve 500 kez çalışan bir döngü, yalnızca zaman damgaları için 500 alt işlem başlatır.
Döngü ek yükünü azaltma stratejileri:
- Değişmeyen komutları döngünün dışına taşıyın (bir kez hesaplayıp yeniden kullanın)
- Aritmetik genişletmeyi
$(( expr ))tercih edin; bu bir yerleşiktir, çatallama değildir - Yalnızca biçimlendirme gerektiğinde
dateçağırmak yerineprintfkullanın - Harici çağrıları toplu hâle getirin: verileri önce toplayın, döngünün dışında bir kez işleyin
#!/usr/bin/env bash
# Demonstrate: compute-once vs fork-per-iteration
# Bad: $(date) forks 1000 times
time (
for i in $(seq 1 1000); do
ts=$(date +%s) # fork each iteration
echo "$i $ts" > /dev/null
done
)
# Good: capture once, reuse
time (
ts=$(date +%s) # fork exactly once
for i in $(seq 1 1000); do
echo "$i $ts" > /dev/null
done
)İşlem Hattı Alt Kabukları ve Değişken Kapsamı Sorunu
bash'te (ksh/zsh'den farklı olarak), bir işlem hattındaki her komut kendi alt kabuğunda çalışır. Bu nedenle bir işlem hattının içinde ayarlanan değişkenler, işlem hattı tamamlandıktan sonra kaybolur.
Bu hem bir doğruluk hatası hem de bir performans sorunudur; veri toplamak amacıyla while read yapısına yönlendirme yapabilir, ancak sonrasında değişkenin boş olduğunu görebilirsiniz.
İki çözüm vardır:
- İşlem ikamesi kullanın:
while read line; do ...; done < <(command)— while döngüsü bir alt kabukta değil, geçerli kabukta çalışır - lastpipe seçeneğini kullanın (
shopt -s lastpipe) — işlem hattının son bölümünü geçerli kabukta çalıştırır (bash 4.2+)
#!/usr/bin/env bash
count=0
# --- Bug: count is always 0 after pipe (subshell) ---
seq 1 5 | while read -r n; do
(( count++ ))
done
echo "After pipe : count=$count" # prints 0
# --- Fix 1: process substitution (no subshell for while) ---
count=0
while read -r n; do
(( count++ ))
done < <(seq 1 5)
echo "Process sub : count=$count" # prints 5
# --- Fix 2: lastpipe option ---
shopt -s lastpipe
count=0
seq 1 5 | while read -r n; do
(( count++ ))
done
echo "lastpipe : count=$count" # prints 5Bir Mikro Karşılaştırmayla Alt Kabuk Maliyetini Ölçme
$(( )) (yerleşik) ile gerçekleştirilen bir aritmetik işlemi, aynı işlemin expr (harici işlem) üzerinden yönlendirilmiş hâliyle karşılaştırarak alt kabuk ek yükünü küçük bir karşılaştırmayla kolayca kanıtlayabilirsiniz.
Tipik bir Linux bilgisayarındaki sonuçlar, expr komutuna yapılan 10.000 çağrının yaklaşık 5 saniye sürdüğünü; aynı sayıda $(( )) çağrısının ise 0,1 saniyeden kısa sürdüğünü gösterir. Bu, aynı çıktı için 50 kat fark demektir.
Bu karşılaştırma kalıbı, herhangi bir iyileştirmeyi ölçmek istediğinizde de kullanışlıdır: her iki sürümü bir döngüde N kez çalıştırın ve time ile karşılaştırın.
#!/usr/bin/env bash
N=500
# External command (fork per call)
time (
x=0
for ((i=0; i<N; i++)); do
x=$(expr $x + 1) # forks expr each time
done
echo "expr result: $x"
)
# Arithmetic builtin (no fork)
time (
x=0
for ((i=0; i<N; i++)); do
(( x++ )) # pure builtin
done
echo "builtin result: $x"
)echo İşlem Hatlarından Kaçınmak için Here-String Kullanma
Bir değişkeni standart girdi olarak beslemek için yaygın olarak echo "$var" | command kalıbı kullanılır. Bu, iki işlem (echo + command) çatallar ve bir işlem hattı oluşturur. Here-string (<<<) aynı sonucu yalnızca bir işlemle elde eder; harici komut, çekirdek tarafından yönetilen geçici bir arabellekten okur.
grep pattern <<< "$var"— bir işlem, işlem hattı yokread -r field1 field2 <<< "$line"— harici araç olmadan bir değişkeni bölmewc -w <<< "$sentence"— bir değişkendeki sözcük sayısı
Here-string'ler, her çatallamanın önemli olduğu sıkı döngülerin içinde özellikle değerlidir.
#!/usr/bin/env bash
data="The quick brown fox"
# --- Fork-heavy: echo spawns a child ---
word_count_slow=$(echo "$data" | wc -w)
echo "Slow word count: $word_count_slow"
# --- Fast: here-string, only wc spawns ---
word_count_fast=$(wc -w <<< "$data")
echo "Fast word count: $word_count_fast"
# --- Even better: use parameter expansion (zero forks) ---
# Split into array, count elements
read -ra words <<< "$data"
echo "Zero-fork count: ${#words[@]}"Uygulamalı Yeniden Düzenleme: Öncesi ve Sonrası
Gerçekçi bir betiğin günlük dosyasını işleyişini inceleyelim ve öğrendiğimiz her şeyi uygulayalım. Özgün sürümde cat, grep, awk ve tr komutları işlem hatlarıyla zincirlenmiştir. Yeniden düzenlenen sürüm, işlem sayısını 8'den 2'ye düşürür.
Yapılan temel değişiklikler:
catkaldırıldı;grepdosyayı doğrudan okuyortr '[:lower:]' '[:upper:]',${var^^}ile değiştirildiecho "$line" | grep -q,[[ $line =~ ]]ile değiştirildi- İşlem hatlı while döngüsü yerine işlem ikamesiyle birlikte
read -rkullanıldı
Yeniden düzenlemeden sonra iyileştirmeyi doğrulamak için time ./script.sh komutunu yeniden çalıştırın. Her zaman ölçün; varsayımda bulunmayın.
#!/usr/bin/env bash
# Create a sample log
printf 'ERROR: disk full\nINFO: started\nERROR: timeout\nINFO: done\n' \
> /tmp/sample.log
# === BEFORE (fork-heavy) ===
time (
cat /tmp/sample.log \
| grep 'ERROR' \
| while read -r line; do
label=$(echo "$line" | tr '[:lower:]' '[:upper:]')
echo "[ALERT] $label"
done
)
# === AFTER (builtin-first) ===
time (
while IFS= read -r line; do
echo "[ALERT] ${line^^}"
done < <(grep 'ERROR' /tmp/sample.log)
)Bilgi Kontrolü: Alt Kabuk Kapsamı
Pipeline alt kabukları hakkındaki anlayışınızı ve bir pipe içinde yapılan değişken değişikliklerinin kaybolmasını nasıl önleyeceğinizi sınayın.
Ders Özeti: Önce Profil Oluşturun, Daha Az Çatallayın
Bu derste bash betiklerindeki gereksiz süreç oluşturmanın en yaygın kaynaklarını nasıl belirleyip ortadan kaldıracağınızı öğrendiniz.
Temel çıkarımlar:
- En iyileştirme yapmadan önce ölçüm almak için
timevePS4ile zenginleştirilmişbash -xkullanın - Gereksiz cat kullanımı en yaygın anti-pattern'dir — dosya adlarını, bunları kabul eden komutlara doğrudan aktarın
echo "$var" | commandyerine here-string (command <<< "$var") veya yerleşik bir komut kullanın- Parametre genişletmeleri (
${var^^},${var//s/r},${#var}), birçoktr,sedvewcçağrısının yerini alır - Pipeline alt kabukları değişken değişikliklerini yutar — süreç ikamesi veya
shopt -s lastpipekullanın - Değişmeyen komut çağrılarını döngülerin dışına taşıyın;
expryerine$(( ))aritmetiğini tercih edin
Pratik kural şudur: önce ölçün, mümkün olduğunda harici komutları yerleşik komutlarla değiştirin ve iyileşmeyi ikinci bir ölçümle doğrulayın.
Sıkça Sorulan Sorular
“Betikleri Profilleme ve Gereksiz Alt Kabuklardan Kaçınma” dersi ücretsiz mi?
Evet — “Betikleri Profilleme ve Gereksiz Alt Kabuklardan Kaçınma” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve Linux Command Line & Bash Scripting Mastery kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. Linux Command Line & Bash Scripting Mastery kursu toplamda 4 dersten oluşur.
“Betikleri Profilleme ve Gereksiz Alt Kabuklardan Kaçınma” dersinde ne öğreneceğim?
Betiklerin çalışma süresini ölçün ve cat-grep zincirleri gibi çok sayıda çatallanma oluşturan kalıpları yerleşik alternatiflerle değiştirin. Linux Command Line & Bash Scripting Mastery ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.
Linux Command Line & Bash Scripting Mastery öğrenmeye başlamak için deneyim gerekli mi?
Önceden deneyim gerekmez. CoddyKit'te Linux Command Line & Bash Scripting Mastery, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 1. dersidir.
“Betikleri Profilleme ve Gereksiz Alt Kabuklardan Kaçınma” dersi ne kadar sürer?
Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.
Bu Linux Command Line & Bash Scripting Mastery dersinde kod yazıp çalıştırabilir miyim?
Evet. Her Linux Command Line & Bash Scripting Mastery dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.
Bu kursun tüm dersleri
- Betikleri Profilleme ve Gereksiz Alt Kabuklardan Kaçınma
- xargs -P ve Arka Plan İşleriyle Paralellik
- GNU parallel ile İş Yüklerini Orkestre Etme
- Veri Akışları ve İş Hacmi için Adlandırılmış Borular