İçindekiler
find ile binlerce dosyayı aradıktan sonra üzerlerinde işlem yapmak istediğimizde iki popüler seçenek karşımıza çıkar: -exec ve xargs. İkisi de bulunan yolları başka bir komuta aktarır; ancak süreç oluşturma maliyeti, özel karakter içeren dosya adları ve güvenilmeyen dizinlerle çalışma konusunda davranışları aynı değildir. Yanlış seçim betiği yalnızca yavaşlatmaz, beklenmedik dosyaların işlenmesine de yol açabilir.
``
Temel çalışma mantığı
Klasik -exec, bulunan her dosya için komutu ayrı çalıştırabilir:
find logs -type f -name '*.log' -exec gzip '{}' \;
Buradaki {} bulunan dosyanın tam yolu ile değiştirilir, \; ise -exec ifadesini sonlandırır. Dosya adı komuta doğrudan bir argüman olarak verildiğinden boşluklar, satır sonları veya joker karakterler yeniden yorumlanmaz.
Buna karşılık xargs, standart girdiden aldığı yolları gruplandırarak komut satırları oluşturur:
find logs -type f -name '*.log' -print0 |
xargs -0 gzip
-print0 yolları NUL karakteriyle ayırır; xargs -0 aynı biçimi okur. Unix dosya adlarında NUL bulunamayacağı için bu ikili, sıra dışı dosya adlarında güvenli bir protokol sağlar.
Performans neden değişiyor?
Bir süreç başlatmanın maliyetini $C_p$, tek bir dosyayı işlemenin maliyetini $C_f$ ve dosya sayısını $n$ kabul edelim. Dosya başına çalışan klasik kullanımın yaklaşık maliyeti:
\[T_{exec} \approx n(C_p + C_f)\]xargs dosyaları ortalama $b$ elemanlık gruplara ayırırsa maliyet yaklaşık olarak şöyledir:
Dolayısıyla kısa süren bir komut binlerce kez çağrıldığında süreç maliyeti belirginleşir. Fakat find modern ve daha güvenli bir toplu çalışma seçeneğine de sahiptir:
find logs -type f -name '*.log' -exec gzip '{}' +
Sondaki +, mümkün olduğunca çok yolu tek komut çağrısında toplar. Böylece xargs benzeri performans elde edilirken ayrı bir boru hattına ihtiyaç kalmaz. Her iki yöntem de işletim sisteminin ARG_MAX sınırını dikkate alarak grupları böler.
| Yöntem | Komut çağrısı | Dosya adı güvenliği | Paralellik |
|---|---|---|---|
-exec ... \; |
Her dosya için bir | Yüksek | Doğrudan yok |
-exec ... + |
Toplu | Yüksek | Doğrudan yok |
xargs |
Toplu | Ayırıcılara bağlı | -P ile var |
-print0 \| xargs -0 |
Toplu | Yüksek | -P ile var |
Güvenlikte asıl tuzaklar
Şu kullanım güvenilir değildir:
find uploads -type f | xargs rm
rapor final.txt iki ayrı argümana bölünebilir. Daha kötüsü, dosya adında satır sonu veya tırnak bulunması ayrıştırmayı bozabilir. Çözüm -print0 | xargs -0 kullanmak ya da doğrudan -exec ... + seçmektir.
xargs kendi başına bir shell başlatmaz; bu nedenle dosya adındaki ; veya $() otomatik olarak çalıştırılmaz. Tehlike genellikle girdiyi metin olarak ayrıştırmaktan veya gereksiz yere sh -c kullanmaktan doğar:
# Güvenilmeyen adları komut metnine gömmeyin
find uploads -type f -exec sh -c "process {}" \;
Bunun yerine yolu konumsal argüman yapın:
find uploads -type f -exec sh -c 'for f; do process "$f"; done' sh '{}' +
Ayrıca bulunan dosya ile işlemin uygulanması arasında dosyanın değiştirilebildiği yarış koşulları her iki yöntemi de etkileyebilir. Güvenilmeyen, başkalarının yazabildiği dizinlerde -execdir komutu dosyanın bulunduğu dizinde çalıştırarak bazı yol değiştirme saldırılarını azaltır. Bununla birlikte güvenli bir PATH ayarlamak gerekir.
Hangisini seçmeli?
Genel amaçlı ve sağlam varsayılan seçenek -exec ... + kullanmaktır: hızlıdır, okunaklıdır ve dosya adlarını doğru taşır. Paralel işlem gerekiyorsa -print0 | xargs -0 -P 4 güçlüdür; ancak komutun eş zamanlı çalışmaya uygun olduğundan emin olun. Her dosyanın ayrı süreçte çalışması özellikle gerekmiyorsa -exec ... \; büyük veri kümelerinde gereksiz derecede pahalıdır.
Yorumlar