Gereksiz kedileri umursamalı mıyım?


50

Bir çok komut satırı yardımcı programı girişlerini bir borudan veya dosya adı argümanı olarak alabilir. Uzun kabuk betikleri için cat, özellikle ilk komutun çok satırlı argümanlara gereksinim duyması durumunda, zinciri başlatmayı daha okunaklı hale getirir buluyorum .

Karşılaştırmak

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

ve

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

İkinci yöntem daha az verimli midir? Eğer öyleyse, betiğin çalıştırılıp çalıştırılmayacağına dikkat etmek için gereken fark, yani saniyede bir kere mi? Okunabilirlik farkı çok büyük değil.


30
İnsanların bu sitedeki işe yaramaz kedi kullanımı konusunda birbirlerine saldırdıklarını
izlememin

4
@Michael:% 100 katılıyorum. Heck, eski usenet ödülü ile bağlantı kurmaktan çok daha fazla zaman aldı, bilgisayarım şimdiye dek başaracak cat. Ancak burada büyük soru olduğunu düşünüyorum kod okunabilirliği sıklıkla olduğu performans üzerinde bir öncelik. Daha hızlı ne zaman daha güzel yazılabilir , neden olmasın? Sorunun belirtilmesi, catgenellikle kullanıcının genel olarak boru hatları ve süreçlerini daha iyi anlamasına neden olur. Bu çabaya değer, bu yüzden bir dahaki sefere anlaşılır kodlar yazıyorlar.
Caleb

3
Aslında, ilk formu sevmemem için başka bir nedenim var - boru hattının başına başka bir komut eklemek isterseniz , argümanı da taşımak zorundasınız, bu nedenle düzenleme daha can sıkıcıdır. (Tabii ki, bu kullanmak zorunda olduğunuz anlamına gelmez cat; Caleb'in işlevleri ve yönlendirmeyi kullanma konusundaki noktası da bunu çözer.)
Cascabel


1
Akşam işe geldi, çalışmam reddediyordu. Stackoverflow'u açıp "Gereksiz kedileri umursamalı mıyım?" Başlıklı bir soru buluyorum. ve bazı evsiz hayvanları ve programcıları,
beslenip

Yanıtlar:


46

"Kesin" cevabı elbette size Yararsız catÖdül Kullanımı tarafından getirilmiştir .

Kedinin amacı, dosyaları birleştirmek (veya "catenate") yapmaktır. Yalnızca bir dosyaysa, hiçbir şeyle birleştirmek bir zaman kaybıdır ve size bir işlem maliyeti gösterir.

Sadece kodunuzu farklı bir şekilde okumak için cat'ı başlatmak, sadece bir işlem daha gerektirmez ve bir tane daha girdi / çıktı akışı yapar. Genelde senaryolarınızdaki gerçek değer yetersiz döngüler ve gerçek işlemlerdir. Çoğu modern sistemde, bir ekstra catperformansınızı öldürmez, ancak kodunuzu yazmanın hemen hemen her zaman başka bir yolu vardır.

Not ettiğiniz gibi çoğu program giriş dosyası için bir argüman kabul edebilir. Bununla birlikte, <bir STDIN akımının beklendiği yerde kullanılabilecek , her zaman çalışmakta olan kabuk işleminde işi yaparak size bir işlem kazandıracak kabuk yerleşimi her zaman vardır .

Nerede yazdığın ile yaratıcı bile olabilirsin. Normal olarak, böyle bir çıkış yönlendirmesi veya borusu belirtmeden önce komutun sonuna yerleştirilir:

sed s/blah/blaha/ < data | pipe

Ancak bu şekilde olmak zorunda değildir. İlk önce bile gelebilir. Örneğin, örnek kodunuz şöyle yazılabilir:

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

Kod okunabilirliği sizin endişeniz ise ve kodunuzun cattakip etmeyi kolaylaştırması için bir satır eklemenin beklendiği kadar karışık olması durumunda, kodunuzu temizlemenin başka yolları da vardır. Daha sonra anlayabilmek için senaryoları kolaylaştıracak bir çok şey kullanıyorum, boruları mantıksal setlere ayırmak ve onları fonksiyonlara kaydetmek. Betik kodu daha sonra doğal hale gelir ve boru hattının herhangi bir bölümünün hata ayıklaması daha kolaydır.

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

fix_blahs < data

Daha sonra devam edebilirsiniz fix_blahs < data | fix_frogs | reorder | format_for_sql. Böyle okuyan bir boru hattının takip edilmesi gerçekten kolaydır ve her bir bileşen ilgili fonksiyonlarında kolayca hata ayıklanabilir.


26
Bunun <fileemirden önce gelebileceğini bilmiyordum . Bu benim tüm sorunlarımı çözer!

3
@Tim: Bash ve Zsh her ikisini de destekliyorlar, ben çirkin olduğunu düşünüyorum. Kodumun güzel ve bakımlı olması konusunda endişelendiğimde, genellikle temizlemek için işlevler kullanıyorum. Son düzenlememe bakın.
Caleb

8
@Tim <filekomut satırında herhangi bir yere gelebilir: <file grep needleveya grep <file needleveya grep needle <file. Bunun istisnası, döngüler ve gruplamalar gibi karmaşık komutlardır; orada yönlendirme done/ }/ )/ etc kapanışından sonra gelmelidir . @Caleb Bu, tüm Bourne / POSIX kabuklarında bulunur. Ve çirkin olduğu konusunda aynı fikirde değilim.
Gilles 'SO- kötülük olmayı'

9
@Gilles, bash $(cat /some/file)ile $(< /some/file)aynı şeyi yapabilirsiniz ancak bir sürecin doğmasını önler.
cjm

3
Sadece bunun $(< /some/file)taşınabilirliği sınırlıdır. Bash olarak çalışır, ancak BusyBox külü veya örneğin FreeBSD sh değildir. Muhtemelen çizgi de çalışmıyor çünkü son üç merminin hepsi kuzen.
dubiousjim

22

İşte bazı dezavantajları bir özeti:

cat $file | cmd

bitmiş

< $file cmd
  • Birincisi, bir not: yukarıda kasıtlı olarak (tartışmanın amacına göre) çift tırnak yoktur $file. Bu durumda cat, bunun dışında daima bir problem vardır zsh; yeniden yönlendirme durumunda, bu yalnızca bashveya ksh88yalnızca ve etkileşimli olduğunda diğer komut kabuklarında (komut dosyalarında değil) sorun olur.
  • En sık alıntı yapılan dezavantaj, ortaya çıkan ekstra işlemdir. Eğer unutmayın cmdyerleşiğidir bazı kabukları gibi de, o bile 2 süreçler var bash.
  • Halen performans cephesinde, catyerleşik kabukları haricinde , ayrıca ekstra bir komut yürütülmekte (ve tabii ki yüklenmekte ve başlatılmaktadır (ve bununla bağlantılı olduğu kütüphaneler)).
  • Yine de performans cephesinde, büyük dosyalar için, sistemin dönüşümlü olarak catve cmdsüreçleri programlaması ve boru tamponunu sürekli doldurması ve boşaltması gerektiği anlamına gelir . Bile cmdyapar 1GBgeniş read()sistem anda çağrıları kontrol arasında ileri ve geri gitmek zorunda kalacak catve cmdbir boru bir anda birkaç veri kilobayt fazla tutamayacak çünkü.
  • Bazıları cmd(gibi wc -c), stdinleri normal bir dosya olduğu zaman bazı optimizasyonlar yapabilir cat | cmd; stdinleri sadece bir pipo olduğu için yapamadıkları normal bir dosyadır . İle catve bir boru, o da bunu yapamazlar demektir seek()dosya içinde. tacVeya gibi komutlar için tail, bu, performansta büyük bir fark yaratır; bu da cat, bütün girişi bellekte saklamaları gerektiği anlamına gelir .
  • cat $fileVe hatta onun daha doğru versiyonu cat -- "$file"gibi bazı özel dosya adları için düzgün çalışmaz -(veya --helpya ile başlayan şey -unutursanız --). Biri kullanmakta ısrar ederse cat, muhtemelen cat < "$file" | cmdgüvenilirlik için kullanması gerekir .
  • Eğer $file(erişim engellendi, ... yok) okumak için açık olamaz, < "$file" cmd(kabuk tarafından) tutarlı bir hata mesajı rapor edecek değil koşmak cmdiken, cat $file | cmdhala çalışır cmdancak boş bir dosya gibi onun Stdin seyir ile. Bu da gibi şeyler demektir < file cmd > file2, file2eğer clobbered değildir fileaçılamaz.

2
Performans ilgili olarak: Bu test akışı üzerinde çok az işlem yapıyoruz sürece fark 1 pct sırasına olduğunu gösterir oletange.blogspot.dk/2013/10/useless-use-of-cat.html
Ole Tange

2
@OleTange. Burada başka bir test var: truncate -s10G a; time wc -c < a; time cat a | wc -c; time cat a | cat | wc -c. Resme giren birçok parametre var. Performans cezası% 0 ile% 100 arasında değişebilir. Her durumda, cezanın negatif olabileceğini düşünmüyorum.
Stéphane Chazelas

2
wc -coldukça benzersiz bir durum, çünkü bir kestirme yolu var. Bunun yerine, wc -wo zaman grepbenim örneğimle karşılaştırılabilir (yani çok az işlem - ki bu, '<' nin bir fark yaratabileceği durumdur ).
Ole Tange

@OleTange, hatta ( wc -wlinux 4.9 ve Cd'deki C yerel ayarında 1GB'lık seyrek bir dosyada) o zaman kedi yaklaşımının çok çekirdekli bir sistemde% 23, bir çekirdeğe bağlarken% 5 daha fazla zaman aldığını buldum. Birden fazla çekirdek tarafından erişilen verilere sahip olunan ek yükü göstermek. Borunun boyutunu değiştirirseniz, farklı veriler kullanırsanız, gerçek I / O kullanarak splice () kullanan bir kedi uygulaması kullanıyorsanız, muhtemelen farklı sonuçlar elde edersiniz. ve bu hiçbir durumda catyardımcı olmaz.
Stéphane Chazelas

1
1GB'lık bir dosya ile benim için wc -wbasit bir grep içine ise yaklaşık% 2 ...% 15 fark. Sonra, garip bir şekilde, eğer bir NFS dosya paylaşımındaysa, eğer yayınlanmışsa okumak için% 20 daha hızlı cat( gist.github.com/rdp/7162414833becbee5919cda855f1cb86 ) Garip ...
rogerdpack

16

<fileBir boru hattının sonuna koymak cat file, başlangıçta olduğundan daha az okunabilir . Doğal İngilizce soldan sağa okur.

<fileDiyelim ki boru hattının başlangıcını koymak kediden daha az okunabilir. Bir sözcük bir sembolden, özellikle yanlış yöne işaret eden bir sembolden daha okunur.

Kullanarak formatı catkorur command | command | command.


Bir <kere kullanmak , kodun daha az okunabilir olmasını sağladığından katılıyorum, çünkü çok yönlünün sözdizimi tutarlılığını yok ediyor.
A.Danischewski

@Jim Okunabilirliği, bunun <gibi bir takma ad oluşturarak çözebilirsiniz : alias load='<'ve ardından eg load file | sed .... Takma adlar çalıştırıldıktan sonra komut dosyalarında kullanılabilir shopt -s expand_aliases.
niieani

1
Evet takma adları biliyorum. Ancak, bu takma ad sembolü bir sözcükle değiştirse de, okuyucunun kişisel takma ad ayarınızı bilmesini gerektirir, bu nedenle çok taşınabilir değildir.
Jim,

8

Buradaki diğer cevapların doğrudan ele almadığı gibi görünen şeylerden biri cat, böyle bir kullanımın “işe yaramaz bir yabancı kedi sürecinin doğduğu” anlamında “işe yaramaz” olmadığı; “Gereksiz iş yapan kedi sürecinin doğurulması” anlamında faydasızdır.

Bu iki durumda:

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

Kabuk, bazı dosyalardan veya stdin'den (sırasıyla) okuyan bir sed işlemini başlatır ve sonra bazı işlemleri yapar - yeni bir satıra gelinceye kadar okur, bu satırdaki ilk 'foo' (varsa) 'bar' ile değiştirir, sonra yazdırır. Bu çizgi stdout ve döngülere.

Bu durumuda:

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

Kabuk, bir kedi süreci ve sed bir işlem ortaya çıkarır ve kedinin sed's std'üne tel bağlar. Kedi işlemi dosyadan bir kaç kilo veya belki de megabayt yığın okur, daha sonra yukarıdaki ikinci örnekte olduğu gibi oradan alınan bir başkasının aldığı yerden stdout'a yazar. Sed bu odun işlerken, kedi başka bir öbek okuyor ve bir sonraki üzerinde çalışması için stdout'una yazıyor.

Başka bir deyişle, catkomut ekleyerek gereken fazladan iş sadece fazladan bir catişlem üretmek için fazladan bir iş değil , aynı zamanda dosyanın baytlarını bir defada iki kez okumak ve yazmaktan başka bir şey değildir. Şimdi, pratik olarak konuşursak ve modern sistemlerde, bu büyük bir fark yaratmıyor - sisteminizi gereksiz yere birkaç mikrosaniye yapar. Ancak, dağıtmayı planladığınız bir senaryo için, potansiyel olarak zaten gücü çok düşük olan makinelerde kullanan insanlar için, birkaç mikrosaniye birçok yinelemenin üzerine ekleyebilir.


2
Bkz oletange.blogspot.dk/2013/10/useless-use-of-cat.html ek kullanmanın yükü bir test için cat.
Ole Tange

@OleTange: Ben sadece bu tökezledi ve blogunuzu ziyaret ettim. (1) İçeriği (çoğunlukla) İngilizce olarak görürken (Sanırım) Danca: “Klassisk”, “Flipcard”, “Magasin”, “Mosaik”, “Sidebjælke”, “Øjebliksbillede” de bir sürü kelime görüyorum. , “Tidsskyder”, “Blog-arkiv”, “Om mig”, “Skrevet” ve “Vis kommentarer” (ancak “Tweet”, “Like” ve çerezler İngilizce'dir). Bunu biliyor muydunuz ve kontrolünüz altında mı? (2) Tablolarınızı okumakta sorun yaşıyorum (2a) çünkü kılavuz çizgileri eksik ve (2b) “Diff (pct)” ile ne demek istediğinizi anlamıyorum.
G-Man 'Monica'yı Yeniden

blogspot.dk Google tarafından işletilmektedir. Blogspot.com ile değiştirmeyi deneyin. "Diff (pct)", ms ile yüzde cinsinden catbölünmüş ms'dir cat(örneğin, 264 ms / 216 ms = 1.22 =% 122 =% 22 ile daha yavaş cat)
Ole Tange
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.