Banyak orang menggunakan oneliners dan skrip yang berisi kode di sepanjang baris
cat "$MYFILE" | command1 | command2 > "$OUTPUT"
Yang pertama cat
sering disebut "penggunaan kucing yang tidak berguna" karena secara teknis ia memerlukan memulai proses baru (sering /usr/bin/cat
) di mana ini dapat dihindari jika perintah telah
< "$MYFILE" command1 | command2 > "$OUTPUT"
karena itu shell hanya perlu memulai command1
dan cukup arahkan stdin
ke file yang diberikan.
Mengapa shell tidak melakukan konversi ini secara otomatis? Saya merasa bahwa sintaks "penggunaan kucing yang tidak berguna" lebih mudah dibaca dan shell harus memiliki informasi yang cukup untuk menyingkirkan kucing yang tidak berguna secara otomatis. Ini cat
didefinisikan dalam standar POSIX sehingga shell harus diizinkan untuk mengimplementasikannya secara internal daripada menggunakan biner di jalur. Shell bahkan bisa memuat implementasi hanya untuk satu versi argumen dan mundur ke biner di jalur.
sumber
lseek
masih didefinisikan perilaku dan dapat menyebabkan hasil yang berbeda, perilaku memblokir yang berbeda dapat bermakna secara semantik, dll. Ini akan diizinkan untuk melakukan perubahan jika Anda tahu apa perintah lain dan tahu mereka tidak peduli, atau jika Anda tidak peduli tentang kompatibilitas pada tingkat itu, tetapi manfaatnya cukup kecil. Saya membayangkan kurangnya manfaat mendorong situasi lebih dari biaya kesesuaian.cat
sendiri, atau utilitas lainnya. Ini juga memungkinkan untuk mengetahui bagaimana utilitas lain yang termasuk ke dalam sistem kerja (misalnya dapat mengetahui bagaimana eksternalgrep
implementasi yang datang dengan sistem berperilaku). Ini benar-benar layak untuk dilakukan, jadi sepenuhnya adil untuk bertanya-tanya mengapa mereka tidak melakukannya.grep
. Dansed
. Danawk
. Dandu
. Dan berapa ratus jika tidak ribuan utilitas lainnya?Jawaban:
2 perintah tidak setara: pertimbangkan penanganan kesalahan:
cat <file that doesn't exist> | less
akan menghasilkan aliran kosong yang akan diteruskan ke program yang disalurkan ... dengan demikian Anda berakhir dengan tampilan yang tidak menunjukkan apa-apa.< <file that doesn't exist> less
akan gagal membuka bilah, dan kemudian tidak membuka sama sekali.Mencoba mengubah yang pertama ke yang kedua dapat mematahkan sejumlah skrip yang berharap untuk menjalankan program dengan input yang berpotensi kosong.
sumber
cat
akan selalu menjalankan perintah kedua dalam pipeline sedangkan varian dengan pengalihan input saja tidak akan menjalankan perintah sama sekali jika file input tidak ada.<"missing-file" grep foo | echo 2
tidak akan mengeksekusigrep
tetapi akan mengeksekusiecho
."Penggunaan yang tidak berguna
cat
" lebih tentang bagaimana Anda menulis kode Anda daripada tentang apa yang sebenarnya berjalan ketika Anda menjalankan skrip. Ini semacam desain anti-pola , cara melakukan sesuatu yang mungkin bisa dilakukan dengan cara yang lebih efisien. Ini adalah kegagalan dalam memahami cara terbaik menggabungkan alat yang diberikan untuk membuat alat baru. Saya berpendapat bahwa merangkai beberapased
dan / atauawk
perintah bersama dalam satu pipeline juga kadang-kadang bisa dikatakan sebagai gejala dari pola anti-sama.Memperbaiki contoh "penggunaan yang tidak berguna
cat
" dalam skrip adalah masalah utama memperbaiki kode sumber skrip secara manual. Alat seperti ShellCheck dapat membantu dengan menunjukkan kasus-kasus nyata:Mendapatkan shell untuk melakukan ini secara otomatis akan sulit karena sifat skrip shell. Cara skrip dijalankan tergantung pada lingkungan yang diwarisi dari proses induknya, dan pada implementasi spesifik dari perintah eksternal yang tersedia.
Shell belum tentu tahu apa
cat
itu. Ini bisa berpotensi perintah apa saja dari mana saja di Anda$PATH
, atau fungsi.Jika itu adalah perintah bawaan (yang mungkin ada di beberapa shell), ia akan memiliki kemampuan untuk mengatur ulang pipa karena akan mengetahui semantik dari
cat
perintah bawaannya. Sebelum melakukan itu, itu juga harus membuat asumsi tentang perintah berikutnya dalam pipa, setelah yang aslicat
.Perhatikan bahwa membaca dari input standar berperilaku sedikit berbeda ketika terhubung ke pipa dan ketika terhubung ke file. Sebuah pipa tidak dapat dicari, jadi tergantung pada apa yang dilakukan perintah berikutnya dalam pipa, itu mungkin atau mungkin tidak berperilaku berbeda jika pipa itu ditata ulang (itu mungkin mendeteksi apakah input dapat dicari dan memutuskan untuk melakukan hal-hal yang berbeda jika itu atau jika tidak, dalam hal apapun itu kemudian akan berperilaku berbeda).
Pertanyaan ini mirip (dalam arti yang sangat umum) dengan " Apakah ada kompiler yang mencoba untuk memperbaiki kesalahan sintaks sendiri? " (Di situs Rekayasa Perangkat Lunak StackExchange), meskipun pertanyaan itu jelas tentang kesalahan sintaks, bukan pola desain yang tidak berguna . Gagasan tentang mengubah kode secara otomatis berdasarkan niat sebagian besar sama.
sumber
cat
itu, dan perintah-perintah lain dalam pipeline, (aturan as-if) dan berperilaku sesuai, mereka tidak di sini karena tidak ada gunanya dan terlalu sulit.cat /dev/tty
adalah yang menarik yang akan berbeda dengannya<
.cat
sebenarnya dilakukan perintah tanpa menjalankannya . Untuk semua yang Anda (dan shell) tahu, OP memiliki perintahcat
di jalurnya yang merupakan simulasi kucing interaktif, "myfile" hanyalah status permainan yang disimpan, dancommand1
dancommand2
sedang memproses beberapa statistik tentang sesi bermain saat ini ...Karena itu tidak sia-sia.
Dalam kasus
cat file | cmd
, fd0
(stdin)cmd
akan berupa pipa, dan dalam kasuscmd <file
itu mungkin berupa file, perangkat, dll.Sebuah pipa memiliki semantik yang berbeda dari file biasa, dan semantiknya bukan bagian dari file biasa:
file biasa tidak dapat
select(2)
diedit ataupoll(2)
diedit dengan cara yang bermakna; aselect(2)
di atasnya akan selalu kembali "siap". Antarmuka tingkat lanjut sepertiepoll(2)
di Linux tidak akan berfungsi dengan file biasa.di Linux ada panggilan sistem (
splice(2)
,vmsplice(2)
,tee(2)
) yang hanya bekerja pada pipa [1]Karena
cat
begitu banyak digunakan, itu dapat diimplementasikan sebagai shell built-in yang akan menghindari proses tambahan, tetapi begitu Anda mulai di jalur itu, hal yang sama dapat dilakukan dengan sebagian besar perintah - mengubah shell menjadi lebih lambat & clunkierperl
ataupython
. mungkin lebih baik menulis bahasa skrip lain dengan sintaksis seperti pipa yang mudah digunakan untuk kelanjutan ;-)[1] Jika Anda ingin contoh sederhana tidak dibuat untuk kesempatan itu, Anda dapat melihat saya "biner exec dari stdin" git inti dengan beberapa penjelasan dalam komentar di sini . Melaksanakan
cat
di dalamnya untuk membuatnya bekerja tanpa UUoC akan membuatnya 2 atau 3 kali lebih besar.sumber
cat
internal.cat /dev/urandom | cpu_bound_program
menjalankanread()
pemanggilan sistem dalam proses terpisah. Di Linux misalnya, pekerjaan CPU yang sebenarnya menghasilkan lebih banyak angka acak (ketika pool kosong) dilakukan dalam system call itu, jadi menggunakan proses yang terpisah memungkinkan Anda memanfaatkan inti CPU yang terpisah untuk menghasilkan data acak sebagai input. misal dalam Apa cara tercepat untuk menghasilkan file teks 1 GB yang berisi angka acak?lseek
tidak akan berhasil.cat foo.mp4 | mpv -
akan berfungsi, tetapi Anda tidak dapat mencari mundur lebih jauh dari buffer cache mpv atau mplayer. Tetapi dengan input yang dialihkan dari suatu file, Anda dapat melakukannya.cat | mpv -
adalah salah satu cara untuk memeriksa apakah MP4 memilikimoov
atomnya di awal file, sehingga dapat diputar tanpa mencari ke belakang dan belakang (yaitu jika itu cocok untuk streaming). Sangat mudah untuk membayangkan kasus-kasus lain di mana Anda ingin menguji suatu program untuk file yang tidak dapat dicari dengan menjalankannya/dev/stdin
dengancat
vs. redirect.xargs cat | somecmd
. Jika jalur file melampaui batas buffer perintah,xargs
dapat berjalancat
beberapa kali menghasilkan aliran berkelanjutan, sementara menggunakanxargs somecmd
secara langsung sering gagal karenasomecmd
tidak dapat dijalankan dalam banyak untuk mencapai hasil yang mulus.Karena mendeteksi kucing yang tidak berguna sangat sulit.
Saya memiliki skrip shell tempat saya menulis
Script shell gagal dalam produksi jika
cat
dihapus karena dipanggil viasu -c 'script.sh' someuser
. Yang tampaknya berlebihancat
menyebabkan pemilik input standar untuk mengubah ke pengguna skrip sedang dijalankan sehingga membuka kembali melalui/proc
bekerja.sumber
cat
diikuti oleh tepat satu parameter sehingga shell harus menggunakancat
executable nyata bukan pintas yang dioptimalkan. Poin bagus tentang kredensial yang mungkin berbeda atau stdin non-standar untuk proses nyata.tl; dr: Kerang tidak melakukannya secara otomatis karena biaya melebihi kemungkinan manfaatnya.
Jawaban lain menunjukkan perbedaan teknis antara stdin menjadi pipa dan menjadi file. Dengan mengingat hal itu, shell dapat melakukan salah satu dari:
cat
sebagai builtin, masih mempertahankan perbedaan file v. Pipe. Ini akan menghemat biaya eksekutif dan mungkin, mungkin garpu.Selanjutnya Anda harus mempertimbangkan biaya dan manfaat dari setiap pendekatan. Manfaatnya cukup sederhana:
cat
)Jadi Anda menghemat sedikit waktu & memori CPU, terutama jika Anda dapat menghindari garpu. Tentu saja, Anda hanya menghemat waktu & memori ini ketika fitur tersebut benar-benar digunakan. Dan Anda hanya benar-benar menghemat waktu fork / exec; dengan file yang lebih besar, waktunya sebagian besar waktu I / O (yaitu, kucing membaca file dari disk). Jadi Anda harus bertanya: seberapa sering
cat
digunakan (tidak berguna) dalam skrip shell di mana kinerja sebenarnya penting? Bandingkan dengan builtin shell umum lainnya sepertitest
- sulit dibayangkancat
digunakan (tidak berguna) bahkan sepersepuluh seringtest
digunakan di tempat-tempat yang penting. Itu dugaan, saya belum mengukur, yang merupakan sesuatu yang ingin Anda lakukan sebelum upaya implementasi. (Atau sama halnya, meminta orang lain untuk mengimplementasikan misalnya permintaan fitur.)Selanjutnya Anda bertanya: berapa biayanya. Dua biaya yang muncul dalam pikiran adalah (a) kode tambahan dalam shell, yang meningkatkan ukurannya (dan dengan demikian mungkin penggunaan memori), membutuhkan lebih banyak pekerjaan pemeliharaan, adalah tempat lain untuk bug, dll .; dan (b) mundur kejutan kompatibilitas, POSIX
cat
menghilangkan banyak fitur misalnya, GNU coreutilscat
, jadi Anda harus berhati-hati persis apa yangcat
akan diterapkan oleh builtin.Opsi builtin tambahan mungkin tidak terlalu buruk - menambahkan satu lagi builtin di mana banyak sudah ada. Jika Anda memiliki data profil yang menunjukkan itu akan membantu, Anda mungkin bisa meyakinkan penulis shell favorit Anda untuk menambahkannya.
Adapun untuk menganalisis pipa, saya tidak berpikir shell melakukan hal seperti ini saat ini (beberapa mengenali ujung pipa dan dapat menghindari percabangan). Pada dasarnya Anda akan menambahkan pengoptimal (primitif) ke shell; pengoptimal sering berubah menjadi kode rumit dan sumber banyak bug. Dan bug-bug itu bisa mengejutkan - sedikit perubahan pada skrip shell dapat berakhir dengan menghindari atau memicu bug.
Catatan tambahan: Anda dapat menerapkan analisis serupa untuk penggunaan kucing yang tidak berguna. Manfaat: lebih mudah dibaca (meskipun jika command1 akan mengambil file sebagai argumen, mungkin tidak). Biaya: garpu dan eksekutif tambahan (dan jika command1 dapat mengambil file sebagai argumen, mungkin pesan kesalahan lebih membingungkan). Jika analisis Anda memberi tahu Anda untuk menggunakan kucing yang tidak berguna, lanjutkan.
sumber
The
cat
perintah dapat menerima-
sebagai penanda untuk stdin . ( POSIX , " Jika file adalah '-', utilitas cat harus membaca dari input standar pada titik itu dalam urutan. ") Ini memungkinkan penanganan sederhana file atau stdin di mana jika tidak maka ini akan dianulir.Pertimbangkan dua alternatif sepele ini, di mana argumen shell
$1
adalah-
:Waktu lain
cat
yang berguna adalah di mana ia sengaja digunakan sebagai larangan untuk mempertahankan sintaksis shell:Akhirnya, saya percaya satu-satunya waktu UUOC benar-benar dapat dipanggil dengan benar adalah ketika
cat
digunakan dengan nama file yang dikenal sebagai file biasa (yaitu bukan perangkat atau pipa bernama), dan bahwa tidak ada bendera yang diberikan kepada perintah:Dalam situasi lain, sifat orop
cat
itu sendiri mungkin diperlukan.sumber
Perintah cat dapat melakukan hal-hal yang tidak dapat dilakukan oleh shell (atau setidaknya, tidak bisa dilakukan dengan mudah). Misalnya, Anda ingin mencetak karakter yang mungkin tidak terlihat, seperti tab, carriage return, atau baris baru. Ada * mungkin * cara untuk melakukannya dengan hanya perintah builtin shell, tapi saya tidak bisa memikirkan apa pun dari atas kepala saya. Versi GNU dari kucing dapat melakukannya dengan
-A
argumen atau-v -E -T
argumen (saya tidak tahu tentang versi kucing yang lain). Anda juga bisa mengawali setiap baris dengan menggunakan nomor baris-n
(sekali lagi, IDK jika versi non-GNU dapat melakukan ini).Keuntungan lain dari kucing adalah dapat dengan mudah membaca banyak file. Untuk melakukannya, seseorang cukup mengetik
cat file1 file2 file3
. Untuk melakukan hal yang sama dengan shell, semuanya akan menjadi rumit, meskipun loop yang dibuat dengan hati-hati kemungkinan besar dapat mencapai hasil yang sama. Yang mengatakan, apakah Anda benar-benar ingin meluangkan waktu untuk menulis loop seperti itu, ketika ada alternatif sederhana seperti itu? Bukan saya!Membaca file dengan cat mungkin akan menggunakan CPU lebih sedikit daripada shell, karena cat adalah program yang sudah dikompilasi (pengecualian yang jelas adalah shell yang memiliki kucing builtin). Saat membaca sekelompok besar file, ini mungkin menjadi jelas, tetapi saya belum pernah melakukannya di komputer saya, jadi saya tidak bisa memastikan.
Perintah cat juga dapat berguna untuk memaksa perintah untuk menerima input standar jika tidak. Pertimbangkan yang berikut ini:
echo 8 | sleep
Angka "8" tidak akan diterima oleh perintah "sleep", karena tidak pernah benar-benar dimaksudkan untuk menerima input standar. Dengan demikian, tidur akan mengabaikan masukan itu, mengeluh tentang kurangnya argumen, dan keluar. Namun, jika satu jenis:
echo 8 | sleep $(cat)
Banyak cangkang akan memperluas ini menjadi
sleep 8
, dan tidur akan menunggu selama 8 detik sebelum keluar. Anda juga dapat melakukan sesuatu yang mirip dengan ssh:command | ssh 1.2.3.4 'cat >> example-file'
Perintah ini dengan menambahkan file-contoh pada mesin dengan alamat 1.2.3.4 dengan apa pun yang dihasilkan dari "perintah".
Dan itu (mungkin) hanya menggaruk permukaan. Saya yakin saya bisa menemukan lebih banyak contoh kucing yang berguna jika saya mau, tetapi postingan ini cukup panjang. Jadi, saya akan menyimpulkan dengan mengatakan ini: meminta shell untuk mengantisipasi semua skenario ini (dan beberapa skenario lainnya) tidak benar-benar layak.
sumber
Ingatlah bahwa seorang pengguna dapat memiliki
cat
di$PATH
dalamnya yang bukan POSIXcat
(tapi mungkin beberapa varian yang dapat mencatat sesuatu di suatu tempat). Dalam hal ini, Anda tidak ingin shell untuk menghapusnya.The
PATH
bisa berubah secara dinamis, dan kemudiancat
bukan apa yang Anda percaya itu. Akan sangat sulit untuk menulis shell yang melakukan optimasi yang Anda impikan.Juga, dalam praktiknya,
cat
adalah program yang cukup cepat. Ada beberapa alasan praktis (kecuali estetika) untuk menghindarinya.Lihat juga pembicaraan hebat Parsing POSIX [s] oleh Yann Regis-Gianas di FOSDEM2018. Ini memberikan alasan bagus lainnya untuk menghindari melakukan apa yang Anda impikan dalam sebuah shell.
Jika kinerja benar-benar masalah untuk shell, seseorang akan mengusulkan shell yang menggunakan optimasi kompiler seluruh program canggih, analisis kode sumber statis, dan teknik kompilasi just-in-time (ketiga domain ini memiliki dekade perkembangan dan publikasi ilmiah dan berdedikasi konferensi, misalnya di bawah SIGPLAN ). Sayangnya, bahkan sebagai topik penelitian yang menarik, yang saat ini tidak didanai oleh lembaga penelitian atau pemodal ventura, dan saya menyimpulkan bahwa itu sama sekali tidak sepadan dengan usaha. Dengan kata lain, mungkin tidak ada pasar yang signifikan untuk mengoptimalkan cangkang . Jika Anda memiliki setengah juta euro untuk dibelanjakan pada penelitian seperti itu, Anda akan dengan mudah menemukan seseorang untuk melakukannya, dan saya percaya itu akan memberikan hasil yang bermanfaat.
Di sisi praktis, menulis ulang, untuk meningkatkan kinerjanya, skrip shell kecil (tidak berbaris) dalam bahasa scripting yang lebih baik (Python, AWK, Guile, ...) umumnya dilakukan. Dan tidak masuk akal (karena banyak alasan rekayasa perangkat lunak) untuk menulis skrip shell besar: ketika Anda menulis skrip shell melebihi seratus baris, Anda perlu mempertimbangkan untuk menulis ulang (bahkan untuk alasan keterbacaan dan pemeliharaan) dalam beberapa bahasa yang lebih cocok : sebagai bahasa pemrograman shell adalah yang sangat miskin. Namun, ada banyak skrip shell yang dihasilkan besar , dan untuk alasan yang baik (misalnya
configure
skrip yang dihasilkan autoconf GNU ).Mengenai file tekstual yang besar, meneruskannya
cat
sebagai argumen tunggal bukanlah praktik yang baik, dan sebagian besar sysadmin tahu bahwa (ketika skrip shell membutuhkan waktu lebih dari satu menit untuk dijalankan, Anda mulai mempertimbangkan untuk mengoptimalkannya). Untuk file gigabyte besar,cat
adalah tidak pernah alat yang baik untuk memproses mereka.sumber
cat some-huge-log | tail -n 5
berlari (di manatail -n 5 some-huge-log
bisa melompat langsung ke akhir, sedangkancat
membaca hanya dari depan ke belakang) akan tidak setuju.cat
file teks besar dalam rentang puluhan GB (yang dibuat untuk pengujian) membutuhkan waktu agak lama. Tidak akan merekomendasikan.Menambahkan ke jawaban @Kusalananda (dan komentar @alephzero), kucing bisa apa saja:
atau
Tidak ada alasan bahwa kucing (sendiri) atau / usr / bin / cat pada sistem sebenarnya adalah alat penggabung.
sumber
cat
didefinisikan oleh POSIX dan karenanya tidak boleh sangat berbeda.PATH=/home/Joshua/bin:$PATH cat ...
Apakah Anda yakin tahu apa yangcat
dilakukan sekarang?cat
bisa ditimpa, tetapi kami juga tahu bahwa itu tidak boleh hanya diganti dengan yang lain. Komentar saya menunjukkan bahwa POSIX mengamanatkan perilaku (subset dari) tertentu yang secara wajar dapat diharapkan ada. Kadang-kadang saya telah menulis skrip shell yang memperluas perilaku utilitas standar. Dalam hal ini skrip shell bertindak dan berperilaku seperti alat yang diganti, kecuali bahwa ia memiliki kemampuan tambahan./bin/cat
. (Dan Anda akan menjadikannya opsi yang bisa Anda matikan.) Atau Anda akan membuatcat
shell built-in (yang mungkin kembali ke/bin/cat
beberapa argumen?) Sehingga pengguna dapat mengontrol apakah mereka ingin versi eksternal normal atau tidak. cara, denganenable cat
. Seperti untukkill
. (Saya berpikir bahwa bashcommand cat
akan berhasil, tetapi itu tidak melewatkan builtin)cat
di lingkungan itu tidak lagi mengacu pada yang biasacat
. Tentunya, optimasi harus diimplementasikan setelah alias diproses. Saya menganggap shell built-in untuk mewakili perintah dalam direktori virtual yang selalu ditambahkan ke jalur Anda. Jika Anda ingin menghindari versi built-in shell dari perintah apa pun (misalnyatest
), Anda harus menggunakan varian dengan path.Dua kegunaan "tidak berguna" untuk kucing:
... di sini
cat
digunakan untuk mencampur input file dan pipa.... di sini
xargs
dapat menerima jumlah nama file yang hampir tak terbatas dan menjalankancat
sebanyak yang diperlukan sambil menjadikan semuanya berperilaku seperti satu aliran. Jadi ini berfungsi untuk daftar file besar di mana penggunaan langsungxargs sort
tidak.sumber
cat
dipanggil dengan satu argumen. Terutama kasus di manash
dilewatkan string danxargs
akan memanggilcat
langsung tidak ada cara shell bisa menggunakan itu built-in implementasi.Selain dari hal-hal lain,
cat
-periksa akan menambah overhead kinerja tambahan dan kebingungan yang penggunaannyacat
sebenarnya tidak berguna, IMHO, karena pemeriksaan seperti itu bisa tidak efisien dan menciptakan masalah dengancat
penggunaan yang sah .Ketika perintah berurusan dengan stream standar, mereka hanya perlu peduli membaca / menulis ke deskriptor file standar. Perintah dapat mengetahui apakah stdin dapat dicari / lseekable atau tidak, yang mengindikasikan pipa atau file.
Jika kita menambahkan ke campuran memeriksa proses apa yang sebenarnya menyediakan konten stdin, kita perlu menemukan proses di sisi lain pipa dan menerapkan optimasi yang sesuai. Ini dapat dilakukan dalam hal shell itu sendiri, seperti yang ditunjukkan dalam posting SuperUser oleh Kyle Jones, dan dalam hal shell itu
seperti yang ditunjukkan pada posting yang ditautkan. Ini adalah 3 perintah lagi (
fork()
s dan s ekstraexec()
) dan traversal rekursif (begitu banyakreaddir()
panggilan).Dalam hal kode sumber C dan shell, shell sudah mengetahui proses anak, jadi tidak perlu untuk rekursi, tetapi bagaimana kita tahu kapan harus mengoptimalkan dan kapan
cat
sebenarnya tidak berguna? Sebenarnya ada kegunaan berguna kucing , sepertiMungkin akan sia-sia dan overhead yang tidak perlu untuk menambahkan optimasi seperti itu ke shell. Seperti jawaban Kusalanda sudah disebutkan, UUOC lebih tentang kurangnya pemahaman pengguna tentang bagaimana cara terbaik menggabungkan perintah untuk hasil terbaik.
sumber