Burada dikkate alınması gereken birkaç nokta var.
i=`cat input`
pahalı olabilir ve mermiler arasında çok fazla varyasyon vardır.
Bu komut yerine koyma adı verilen bir özellik. Fikir, komutun tüm çıktısını eksi sondaki yeni satır karakterlerini i
bellekteki değişkene depolamaktır.
Bunu yapmak için, kabuklar komutu bir alt kabukta çatallar ve çıkışını bir boru veya soket çifti aracılığıyla okur. Burada çok fazla varyasyon görüyorsunuz. Buradaki bir 50MiB dosyasında, örneğin bash'ın ksh93'ten 6 kat daha yavaş, ancak zsh'den biraz daha hızlı ve iki kat daha hızlı olduğunu görebiliyorum yash
.
bash
Yavaş olmanın ana nedeni , bir seferde 128 bayt borudan (diğer kabuklar bir kerede 4KiB veya 8KiB okurken) okuması ve sistem çağrısı yükü tarafından cezalandırılmasıdır.
zsh
NUL baytlarından (diğer mermiler NUL baytlarında kırılır) kaçmak için bazı son işlemler yapması gerekir ve yash
çok baytlı karakterleri ayrıştırarak daha da ağır iş işleme yapar.
Tüm mermilerin az ya da çok verimli bir şekilde yapabilecekleri son satırsonu karakterlerini soyması gerekir.
Bazıları NUL baytlarını diğerlerinden daha zarif işlemek ve varlığını kontrol etmek isteyebilir.
Daha sonra bellekte bu büyük değişkene sahip olduğunuzda, üzerindeki herhangi bir manipülasyon genellikle daha fazla bellek tahsis etmeyi ve veriyi başa çıkmayı içerir.
Burada, değişkenin içeriğini geçiyorsunuz (geçmeyi düşünüyordunuz) echo
.
Neyse ki, echo
kabuğunuzda yerleşiktir, aksi takdirde yürütme büyük olasılıkla bir arg listesi çok uzun bir hata ile başarısız olurdu . O zaman bile, argüman listesi dizisini oluşturmak muhtemelen değişkenin içeriğini kopyalamayı içerecektir.
Komut değiştirme yaklaşımınızdaki diğer temel sorun, split + glob operatörünü çağırmanızdır (değişkeni alıntılamayı unutarak).
Bunun için, mermilerin dizgiye bir karakter dizisi olarak davranması gerekir (bazı mermiler bu bağlamda yoktur ve buggy olsa da), bu yüzden UTF-8 yerellerinde, UTF-8 dizilerini ayrıştırmak anlamına gelir (zaten olduğu gibi yapılmadıysa yash
) , $IFS
dizedeki karakterleri arayın . Eğer $IFS
boşluk, (varsayılan olarak böyledir) sekmesi veya satır başı karakteri içeren, algoritma daha da karmaşık ve pahalıdır. Daha sonra, bu bölünmeden kaynaklanan kelimelerin tahsis edilmesi ve kopyalanması gerekir.
Glob kısmı daha pahalı olacak. Bu kelimelerin herhangi glob karakterler içeriyorsa ( *
, ?
, [
), sonra kabuk bazı dizinlerin içeriğini okumak ve bazı pahalı desen eşleştirme yapmak zorunda olacak ( bash
örneğin 'ın uygulama Şuna çok kötü bir iştir).
Girdi gibi bir şey içeriyorsa /*/*/*/../../../*/*/*/../../../*/*/*
, bu binlerce dizin listelemek anlamına gelir ve bu da birkaç yüz MiB'ye kadar genişleyebilir.
Sonra echo
tipik olarak bazı ekstra işlemler yapar. Bazı uygulamalar \x
, aldığı argümandaki dizileri genişletir , bu da içeriği ayrıştırmak ve muhtemelen verilerin başka bir tahsisi ve kopyası anlamına gelir.
Öte yandan, Tamam, çoğu kabukta cat
yerleşik değildir, bu nedenle bir işlemin istenmesi ve yürütülmesi (kodun ve kütüphanelerin yüklenmesi), ancak ilk çağrıldıktan sonra, bu kod ve giriş dosyasının içeriği anlamına gelir bellekte önbelleğe alınır. Öte yandan, aracı olmayacak. cat
tek seferde büyük miktarlar okuyacak ve işlemeden hemen yazacak ve büyük miktarda bellek ayırmasına gerek yok, sadece yeniden kullandığı bir tampon.
Ayrıca, NUL baytlarını boğmadığı ve sondaki yeni satır karakterlerini kırpmadığı (ve split + glob yapmadığı için çok daha güvenilir olduğu anlamına gelir, ancak değişkeni alıntılayarak bundan kaçınabilirsiniz ve kaçış dizisini genişletmek printf
yerine echo
) kullanmaktan kaçınabilirsiniz .
Yerine çağırmak, daha da optimize etmek istiyorsanız cat
birkaç kez, sadece geçmesi input
için birkaç kez cat
.
yes input | head -n 100 | xargs cat
100 yerine 3 komut çalıştırır.
Değişken sürümü daha güvenilir hale getirmek için kullanmanız gerekir zsh
(diğer kabuklar NUL baytlarıyla baş edemez) ve bunu yapmanız gerekir:
zmodload zsh/mapfile
var=$mapfile[input]
repeat 10 print -rn -- "$var"
Girişin NUL bayt içermediğini biliyorsanız, bunu güvenilir bir şekilde POSIXly yapabilirsiniz (ancak printf
yerleşik olmayan yerlerde çalışmayabilir ):
i=$(cat input && echo .) || exit # add an extra .\n to avoid trimming newlines
i=${i%.} # remove that trailing dot (the \n was removed by cmdsubst)
n=10
while [ "$n" -gt 10 ]; do
printf %s "$i"
n=$((n - 1))
done
Ancak bu, cat
döngüde kullanmaktan daha verimli olmayacaktır (giriş çok küçük olmadığı sürece).
cat $(for i in $(seq 1 10); do echo "input"; done) >> output
? :)