Ada beberapa hal yang perlu dipertimbangkan di sini.
i=`cat input`
bisa mahal dan ada banyak variasi di antara cangkang.
Itu fitur yang disebut substitusi perintah. Idenya adalah untuk menyimpan seluruh output dari perintah dikurangi karakter baris baru ke dalam i
variabel dalam memori.
Untuk melakukan itu, shells fork perintah dalam subkulit dan membaca outputnya melalui pipa atau soketpair. Anda melihat banyak variasi di sini. Pada file 50MiB di sini, saya dapat melihat misalnya bash menjadi 6 kali lebih lambat dari ksh93 tetapi sedikit lebih cepat dari zsh dan dua kali lebih cepat yash
.
Alasan utama untuk bash
menjadi lambat adalah bahwa ia membaca dari 128 byte pipa sekaligus (sementara shell lain membaca 4KiB atau 8KiB pada suatu waktu) dan dihukum oleh overhead panggilan sistem.
zsh
perlu melakukan beberapa post-processing untuk keluar dari NUL byte (shell lain memecah NUL bytes), dan yash
melakukan lebih banyak tugas berat dengan mem-parsing karakter multi-byte.
Semua shell perlu menghapus karakter baris baru yang mungkin mereka lakukan lebih atau kurang efisien.
Beberapa mungkin ingin menangani byte NUL lebih anggun daripada yang lain dan memeriksa keberadaan mereka.
Kemudian setelah Anda memiliki variabel besar dalam memori, manipulasi apa pun pada umumnya melibatkan mengalokasikan lebih banyak memori dan mengatasi data.
Di sini, Anda mengirim (bermaksud untuk mengirimkan) konten variabel ke echo
.
Untungnya, echo
sudah ada di dalam shell Anda, jika tidak eksekusi akan gagal dengan daftar argumen yang terlalu panjang . Bahkan kemudian, membangun array daftar argumen mungkin akan melibatkan menyalin konten variabel.
Masalah utama lainnya dalam pendekatan substitusi perintah Anda adalah bahwa Anda menjalankan operator split + glob (dengan lupa mengutip variabel).
Untuk itu, shell perlu memperlakukan string sebagai string karakter (meskipun beberapa shell tidak dan bermasalah dalam hal itu) sehingga dalam lokal UTF-8, itu berarti menguraikan urutan UTF-8 (jika tidak dilakukan sudah seperti yang yash
dilakukan) , cari $IFS
karakter dalam string. Jika $IFS
berisi spasi, tab, atau baris baru (yang merupakan kasus secara default), algoritme itu bahkan lebih kompleks dan mahal. Kemudian, kata-kata yang dihasilkan dari pemisahan itu perlu dialokasikan dan disalin.
Bagian gumpalan akan lebih mahal. Jika ada kata-kata berisi karakter gumpal ( *
, ?
, [
), maka shell akan harus membaca isi dari beberapa direktori dan melakukan beberapa pencocokan pola mahal ( bash
's pelaksanaan misalnya ini sangat sangat buruk pada saat itu).
Jika input berisi sesuatu seperti /*/*/*/../../../*/*/*/../../../*/*/*
, itu akan sangat mahal karena itu berarti daftar ribuan direktori dan yang dapat berkembang hingga beberapa ratus MiB.
Maka echo
biasanya akan melakukan beberapa pemrosesan tambahan. Beberapa implementasi memperluas \x
urutan dalam argumen yang diterimanya, yang berarti mengurai konten dan mungkin alokasi lain dan menyalin data.
Di sisi lain, OK, dalam kebanyakan shell cat
tidak built-in, sehingga itu berarti forking proses dan mengeksekusinya (jadi memuat kode dan pustaka), tetapi setelah doa pertama, kode itu dan isi file input akan di-cache dalam memori. Di sisi lain, tidak akan ada perantara. cat
akan membaca sejumlah besar dalam satu waktu dan menulisnya langsung tanpa diproses, dan tidak perlu mengalokasikan sejumlah besar memori, hanya satu buffer yang digunakan kembali.
Ini juga berarti bahwa itu jauh lebih dapat diandalkan karena tidak tersedak NUL byte dan tidak memotong karakter baris baru (dan tidak melakukan split + glob, meskipun Anda dapat menghindarinya dengan mengutip variabel, dan tidak perluas urutan escape meskipun Anda bisa menghindarinya dengan menggunakan printf
alih-alih echo
).
Jika Anda ingin mengoptimalkannya lebih lanjut, alih-alih memohon cat
beberapa kali, berikan saja input
beberapa kali cat
.
yes input | head -n 100 | xargs cat
Akan menjalankan 3 perintah, bukan 100.
Untuk membuat versi variabel lebih dapat diandalkan, Anda harus menggunakan zsh
(shell lain tidak dapat mengatasi byte NUL) dan melakukannya:
zmodload zsh/mapfile
var=$mapfile[input]
repeat 10 print -rn -- "$var"
Jika Anda tahu input tidak mengandung byte NUL, maka Anda dapat melakukannya dengan POSIXly (meskipun mungkin tidak berfungsi di tempat printf
yang tidak dibangun) dengan:
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
Tapi itu tidak akan pernah lebih efisien daripada menggunakan cat
dalam loop (kecuali inputnya sangat kecil).
cat $(for i in $(seq 1 10); do echo "input"; done) >> output
? :)