Haruskah saya peduli dengan kucing yang tidak perlu?

50

Banyak utilitas baris perintah dapat mengambil inputnya baik dari pipa atau sebagai argumen nama file. Untuk skrip shell panjang, saya menemukan memulai rantai dengan catmembuatnya lebih mudah dibaca, terutama jika perintah pertama akan membutuhkan argumen multi-line.

Membandingkan

sed s/bla/blaha/ data \
| grep blah \
| grep -n babla

dan

cat data \
| sed s/bla/blaha/ \
| grep blah \
| grep -n babla

Apakah metode yang terakhir kurang efisien? Jika demikian, apakah perbedaannya cukup untuk dipedulikan jika skrip dijalankan, katakanlah, satu detik sekali? Perbedaan dalam keterbacaan tidak besar.

tepang
sumber
30
Saya menghabiskan lebih banyak waktu menonton orang saling menyerang tentang penggunaan kucing yang tidak berguna di situs ini daripada sistem saya yang sebenarnya memulai proses kucing
Michael Mrozek
4
@Michael: 100% setuju. Heck butuh lebih banyak waktu untuk menghubungkan ke penghargaan usenet lama sekali daripada komputer saya akan pernah buang instantiating cat. Namun saya pikir pertanyaan yang lebih besar di sini adalah pembacaan kode yang sering merupakan prioritas dibandingkan kinerja. Ketika lebih cepat sebenarnya bisa ditulis lebih cantik , mengapa tidak? Menunjukkan masalah dengan catbiasanya menyebabkan pengguna memiliki pemahaman yang lebih baik tentang saluran pipa dan proses secara umum. Ini sepadan dengan usaha sehingga mereka menulis kode yang dapat dipahami di waktu berikutnya.
Caleb
3
Saya sebenarnya punya alasan lain saya tidak suka formulir pertama - jika Anda ingin menambahkan perintah lain di awal pipa, Anda harus memindahkan argumen juga, sehingga pengeditan lebih mengganggu. (Tentu saja, ini tidak berarti Anda harus menggunakan cat; Poin Caleb tentang menggunakan fungsi dan pengalihan menyelesaikan juga.)
Cascabel
Terkait: Hapus kucing yang tidak berguna atau tidak?   (Meta)
G-Man Mengatakan 'Reinstate Monica'
1
Ini malam di tempat kerja, had saya menolak untuk bekerja. Saya membuka stackoverflow dan menemukan pertanyaan, berjudul "Haruskah saya peduli dengan kucing yang tidak perlu?" dan melihat beberapa hewan tunawisma dan seorang programmer, merenungkan tentang memberi makan mereka atau tidak ...
Boris Burkov

Jawaban:

46

Jawaban "pasti" tentu saja diajukan kepada Anda oleh The Use of catAward yang Tidak Berguna .

Tujuan dari kucing adalah untuk menggabungkan file (atau "catenate"). Jika hanya satu file, menggabungkannya dengan tidak sama sekali adalah buang-buang waktu, dan biaya proses.

Instantiating cat hanya agar kode Anda membaca berbeda membuat hanya satu proses lagi dan satu set input / output stream yang tidak diperlukan. Biasanya penangguhan nyata dalam skrip Anda akan menjadi loop yang tidak efisien dan pemrosesan aktual. Pada kebanyakan sistem modern, satu tambahan cattidak akan mematikan kinerja Anda, tetapi hampir selalu ada cara lain untuk menulis kode Anda.

Sebagian besar program, seperti yang Anda perhatikan, dapat menerima argumen untuk file input. Namun, selalu ada builtin shell <yang dapat digunakan di mana pun aliran STDIN diharapkan yang akan menyelamatkan Anda satu proses dengan melakukan pekerjaan dalam proses shell yang sudah berjalan.

Anda bahkan dapat berkreasi dengan DIMANA Anda menulisnya. Biasanya itu akan ditempatkan di akhir perintah sebelum Anda menentukan pengalihan atau pipa keluaran seperti ini:

sed s/blah/blaha/ < data | pipe

Tetapi tidak harus seperti itu. Bahkan bisa didahulukan. Misalnya kode contoh Anda dapat ditulis seperti ini:

< data \
    sed s/bla/blaha/ |
    grep blah |
    grep -n babla

Jika keterbacaan skrip menjadi perhatian Anda dan kode Anda cukup berantakan sehingga menambahkan baris untuk catdiharapkan untuk membuatnya lebih mudah diikuti, ada cara lain untuk membersihkan kode Anda. Salah satu yang saya gunakan banyak yang membantu membuat skrip mudah untuk mencari tahu nanti adalah memecah pipa menjadi set logis dan menyimpannya dalam fungsi. Kode skrip kemudian menjadi sangat alami, dan salah satu bagian dari pipline lebih mudah di-debug.

function fix_blahs () {
    sed s/bla/blaha/ |
    grep blah |
    grep -n babla
}

fix_blahs < data

Anda kemudian dapat melanjutkan fix_blahs < data | fix_frogs | reorder | format_for_sql. Sebuah pipleline yang bertuliskan seperti itu sangat mudah diikuti, dan masing-masing komponen dapat di-debug dengan mudah di fungsinya masing-masing.

Caleb
sumber
26
Saya tidak tahu itu <filebisa datang sebelum perintah. Ini menyelesaikan semua masalah saya!
3
@Tim: Bash dan Zsh sama-sama mendukung itu, meskipun saya pikir itu jelek. Ketika saya khawatir tentang kode saya yang cantik dan terpelihara saya biasanya menggunakan fungsi untuk membersihkannya. Lihat hasil edit terakhir saya.
Caleb
8
@Tim <filedapat datang ke mana saja di baris perintah: <file grep needleatau grep <file needleatau grep needle <file. Pengecualian adalah perintah kompleks seperti loop dan pengelompokan; di sana pengalihan harus dilakukan setelah penutupan done/ }/ )/ etc. @ Caleb Ini berlaku untuk semua cangkang Bourne / POSIX. Dan saya tidak setuju bahwa itu jelek.
Gilles 'SANGAT berhenti menjadi jahat'
9
@Gilles, dalam bash Anda dapat menggantinya $(cat /some/file)dengan $(< /some/file), yang melakukan hal yang sama tetapi menghindari proses pemijahan.
cjm
3
Hanya untuk mengkonfirmasi bahwa $(< /some/file)portabilitasnya terbatas. Itu berfungsi di bash, tetapi tidak BusyBox ash, misalnya, atau FreeBSD sh. Mungkin juga tidak bekerja dengan cepat, karena ketiga kerang terakhir itu adalah sepupu dekat.
dubiousjim
22

Berikut ringkasan dari beberapa kekurangan:

cat $file | cmd

lebih

< $file cmd
  • Pertama, sebuah catatan: ada (sengaja untuk tujuan diskusi) ada tanda kutip ganda di $fileatas. Dalam kasus cat, itu selalu menjadi masalah kecuali untuk zsh; dalam kasus pengalihan, itu hanya masalah untuk bashatau ksh88dan, untuk beberapa shell lain hanya ketika interaktif (bukan dalam skrip).
  • Kelemahan yang paling sering dikutip adalah proses ekstra yang dihasilkan. Perhatikan bahwa jika cmdbuiltin, itu bahkan 2 proses di beberapa shell seperti bash.
  • Masih di depan kinerja, kecuali di shell di mana catbuiltin, itu juga perintah tambahan dieksekusi (dan tentu saja dimuat, dan diinisialisasi (dan perpustakaan itu terhubung juga)).
  • Masih di depan kinerja, untuk file besar, itu berarti sistem harus menjadwalkan catdan cmdmemproses secara bergantian dan terus-menerus mengisi dan mengosongkan buffer pipa. Bahkan jika cmdtidak 1GBbesar read()panggilan sistem pada suatu waktu, kontrol harus bolak-balik antara catdan cmdkarena pipa tidak bisa menahan lebih dari beberapa kilobyte data pada suatu waktu.
  • Beberapa cmd(seperti wc -c) dapat melakukan beberapa optimasi ketika stdin mereka adalah file biasa yang tidak dapat mereka lakukan cat | cmdkarena stdin mereka hanyalah sebuah pipa saja. Dengan catdan sebuah pipa, itu juga berarti mereka tidak dapat seek()berada di dalam file. Untuk perintah seperti tacatau tail, itu membuat perbedaan besar dalam kinerja karena itu berarti bahwa catmereka perlu menyimpan seluruh input dalam memori.
  • Versi cat $file, dan bahkan versi yang lebih benar cat -- "$file"tidak akan berfungsi dengan baik untuk beberapa nama file tertentu seperti -( --helpatau apa pun yang dimulai dengan -jika Anda lupa --). Jika seseorang bersikeras menggunakan cat, ia mungkin harus menggunakan cat < "$file" | cmdsebagai gantinya untuk keandalan.
  • Jika $filetidak dapat dibuka untuk dibaca (akses ditolak, tidak ada ...), < "$file" cmdakan melaporkan pesan kesalahan yang konsisten (oleh shell) dan tidak berjalan cmd, sementara cat $file | cmdmasih akan berjalan cmdtetapi dengan stdin yang terlihat seperti itu adalah file kosong. Itu juga berarti bahwa dalam hal-hal seperti < file cmd > file2, file2tidak musnah jika filetidak bisa dibuka.
Stéphane Chazelas
sumber
2
Mengenai kinerja: Tes ini menunjukkan perbedaannya dalam urutan 1 persen kecuali jika Anda melakukan pemrosesan yang sangat sedikit pada aliran oletange.blogspot.dk/2013/10/useless-use-of-cat.html
Ole Tange
2
@OleTange. Berikut tes lain: truncate -s10G a; time wc -c < a; time cat a | wc -c; time cat a | cat | wc -c. Ada banyak parameter yang masuk ke dalam gambar. Penalti kinerja dapat berubah dari 0 hingga 100%. Bagaimanapun, saya tidak berpikir hukumannya bisa negatif.
Stéphane Chazelas
2
wc -cadalah kasus yang cukup unik, karena memiliki jalan pintas. Jika Anda melakukannya wc -wmaka itu dapat dibandingkan dengan grepdalam contoh saya (yaitu sangat sedikit pemrosesan - yang merupakan situasi di mana '<' dapat membuat perbedaan).
Ole Tange
@OleTange, bahkan ( wc -wpada file jarang 1GB di lokal C di linux 4.9 amd64) maka saya menemukan pendekatan kucing membutuhkan 23% lebih banyak waktu ketika pada sistem multicore dan 5% ketika mengikat mereka ke satu inti. Menampilkan overhead tambahan yang dikeluarkan dengan memiliki data yang diakses oleh lebih dari satu inti. Anda mungkin akan mendapatkan hasil yang berbeda jika Anda mengubah ukuran pipa, menggunakan data yang berbeda, melibatkan I / O nyata menggunakan implementasi kucing yang menggunakan splice () ... Semua mengonfirmasi bahwa ada banyak parameter yang didapat dalam gambar dan dalam hal apa pun cattidak akan membantu.
Stéphane Chazelas
1
Bagi saya dengan file 1GB wc -witu perbedaan sekitar 2% ... perbedaan 15% jika itu menjadi grep sederhana lurus. Kemudian, anehnya, jika itu ada di berbagi file NFS itu sebenarnya 20% lebih cepat untuk membacanya jika disalurkan dari cat( gist.github.com/rdp/7162414833becbee5919cda855f1cb86 ) Aneh ...
rogerdpack
16

Memasang <fileujung pipa kurang bisa dibaca daripada memiliki cat filedi awal. Bahasa Inggris Alami berbunyi dari kiri ke kanan.

Menempatkan <fileawal pipa juga kurang dapat dibaca daripada kucing, saya akan mengatakan. Sebuah kata lebih mudah dibaca daripada simbol, terutama simbol yang sepertinya menunjuk ke arah yang salah.

Menggunakan catmempertahankan command | command | commandformat.

Jim
sumber
Saya setuju, menggunakan <sekali membuat kode kurang mudah dibaca, karena merusak konsistensi sintaksis dari sebuah multipipeline.
A.Danischewski
@ Jim Anda dapat memecahkan keterbacaan dengan membuat alias untuk <menyukai ini: alias load='<'dan kemudian gunakan misalnya load file | sed .... Alias ​​dapat digunakan dalam skrip setelah dijalankan shopt -s expand_aliases.
niieani
1
Ya saya tahu tentang alias. Namun, meskipun alias ini menggantikan simbol dengan kata, itu mengharuskan pembaca untuk mengetahui tentang pengaturan alias pribadi Anda, jadi tidak terlalu portabel.
Jim
8

Satu hal yang tampaknya tidak dijawab langsung oleh jawaban lain di sini adalah bahwa menggunakan catseperti ini bukan "tidak berguna" dalam arti bahwa "proses kucing asing muncul yang tidak bekerja"; tidak ada gunanya dalam arti "proses kucing muncul yang hanya bekerja tidak perlu".

Dalam hal ini keduanya:

sed 's/foo/bar/' somefile
<somefile sed 's/foo/bar/'

shell memulai proses sed yang membaca dari somefile atau stdin (masing-masing) dan kemudian melakukan beberapa pemrosesan - ia membaca sampai menyentuh baris baru, menggantikan 'foo' pertama (jika ada) pada baris itu dengan 'bar', kemudian mencetak baris ke stdout dan loop.

Dalam kasus:

cat somefile | sed 's/foo/bar/'

Shell memunculkan proses kucing dan proses sed, dan kabel stdout kucing ke stdin sed. Proses cat membaca beberapa kilo atau mungkin potongan mega-byte dari file, kemudian menuliskannya ke stdout, di mana perintah sed mengambil dari sana seperti pada contoh kedua di atas. Sementara sed sedang memproses potongan itu, kucing membaca potongan lain dan menulisnya di stdout untuk sed untuk mengerjakan selanjutnya.

Dengan kata lain, pekerjaan tambahan yang diperlukan dengan menambahkan catperintah bukan hanya pekerjaan ekstra untuk menghasilkan catproses tambahan , tetapi juga pekerjaan ekstra membaca dan menulis byte file dua kali alih-alih sekali. Sekarang, secara praktis berbicara dan pada sistem modern, itu tidak membuat perbedaan besar - itu mungkin membuat sistem Anda melakukan beberapa mikrodetik dari pekerjaan yang tidak perlu. Tetapi jika itu untuk skrip yang Anda rencanakan untuk didistribusikan, berpotensi untuk orang yang menggunakannya pada mesin yang sudah kurang bertenaga, beberapa mikrodetik dapat menambahkan lebih banyak iterasi.

godlygeek
sumber
2
Lihat oletange.blogspot.dk/2013/10/useless-use-of-cat.html untuk pengujian overhead menggunakan tambahan cat.
Ole Tange
@OleTange: Saya baru saja menemukan ini, dan mengunjungi blog Anda. (1) Sementara saya melihat konten (kebanyakan) dalam bahasa Inggris, saya melihat banyak kata dalam (saya kira) bahasa Denmark: “Klassisk”, “Flipcard”, “Magasin”, “Magaik”, “Mosaik”, “Sidebjælke”, “Øjebliksbillede” , "Tidsskyder", "Blog-arkiv", "Om mig", "Skrevet", dan "Vis kommentarer" (tapi "Tweet", "Like", dan spanduk cookie dalam bahasa Inggris). Apakah Anda tahu tentang ini, dan apakah itu di bawah kendali Anda? (2) Saya kesulitan membaca tabel Anda (2a) karena garis kisi tidak lengkap, dan (2b) Saya tidak mengerti apa yang Anda maksud dengan “Diff (pct)”.
G-Man Mengatakan 'Reinstate Monica'
blogspot.dk dijalankan oleh Google. Coba ganti dengan blogspot.com. The "Diff (pct)" adalah ms dengan catdibagi dengan ms tanpa catdalam persen (mis 264 ms / 216 ms = 1.22 = 122% = 22% lebih lambat dengan cat)
Ole Tange