Sollte ich mich um unnötige Katzen kümmern?


50

Viele Befehlszeilenprogramme können ihre Eingabe entweder über eine Pipe oder als Dateinamenargument verwenden. Bei langen Shell-Skripten ist das Starten der Kette mit einem catbesser lesbar, insbesondere wenn der erste Befehl mehrzeilige Argumente benötigt.

Vergleichen Sie

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

und

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

Ist die letztere Methode weniger effizient? Wenn ja, ist der Unterschied ausreichend, um zu berücksichtigen, ob das Skript beispielsweise einmal pro Sekunde ausgeführt wird? Der Unterschied in der Lesbarkeit ist nicht groß.


30
Ich verbringe viel mehr Zeit damit, Menschen zu beobachten, die sich gegenseitig wegen nutzloser Katzennutzung auf dieser Site angreifen, als mein System tatsächlich die Katzennutzungsprozesse startet
Michael Mrozek

4
@ Michael: 100% stimmen zu. Ich habe mehr Zeit gebraucht, um mich einmal mit der alten Usenet-Auszeichnung zu verbinden, als mein Computer jemals mit der Instantiierung verschwenden würde cat. Aber ich denke , die größere Frage ist hier die Lesbarkeit des Codes , die häufig ist eine Priorität über die Leistung. Wenn schneller eigentlich schöner geschrieben werden kann , warum nicht? Das Hervorheben des Problems catführt in der Regel dazu, dass der Benutzer die Pipelines und Prozesse im Allgemeinen besser versteht. Die Mühe lohnt sich, damit sie das nächste Mal verständlichen Code schreiben.
Caleb

3
Ich habe tatsächlich einen weiteren Grund, warum mir das erste Formular nicht gefällt. Wenn Sie am Anfang der Pipeline einen weiteren Befehl hinzufügen möchten , müssen Sie auch das Argument verschieben, damit die Bearbeitung ärgerlicher wird. (Natürlich heißt das nicht, dass Sie verwenden müssen cat. Calebs Hinweis zur Verwendung von Funktionen und Umleitung löst dies ebenfalls.)
Cascabel,


1
Es ist Abend bei der Arbeit, mein Vater weigert sich zu arbeiten. Ich öffne stackoverflow und finde eine Frage mit dem Titel "Sollte ich mich um unnötige Katzen kümmern?". und einige obdachlose Tiere und einen Programmierer zu sehen, die darüber nachdenken, sie zu füttern oder nicht ...
Boris Burkov

Antworten:


46

Die "endgültige" Antwort wird Ihnen natürlich von The Useless Use of catAward gebracht .

Der Zweck von cat besteht darin, Dateien zu verketten (oder "zu verketten"). Wenn es sich nur um eine Datei handelt, ist das Verketten mit nichts Zeitverschwendung und kostet Sie einen Prozess.

Wenn Sie cat instanziieren, damit Ihr Code anders gelesen wird, ist dies nur ein weiterer Prozess und eine weitere Reihe von Eingabe- / Ausgabestreams, die nicht benötigt werden. Normalerweise wird der eigentliche Stillstand in Ihren Skripten in ineffizienten Schleifen und aktueller Verarbeitung liegen. Auf den meisten modernen Systemen wird eine zusätzliche catLeistung Ihre Leistung nicht beeinträchtigen, aber es gibt fast immer eine andere Möglichkeit, Ihren Code zu schreiben.

Wie Sie bemerken, können die meisten Programme ein Argument für die Eingabedatei akzeptieren. Es gibt jedoch immer die eingebaute Shell <, die überall dort verwendet werden kann, wo ein STDIN-Stream erwartet wird, der Ihnen einen Prozess erspart, indem Sie die Arbeit in dem Shell-Prozess ausführen, der bereits ausgeführt wird.

Sie können sogar kreativ werden, wo Sie es schreiben. Normalerweise wird es am Ende eines Befehls platziert, bevor Sie Ausgabeumleitungen oder Pipes wie folgt angeben:

sed s/blah/blaha/ < data | pipe

Das muss aber nicht so sein. Es kann sogar zuerst kommen. Zum Beispiel könnte Ihr Beispielcode so geschrieben werden:

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

Wenn die Lesbarkeit von Skripten Ihr Anliegen ist und Ihr Code so unübersichtlich ist, dass das Hinzufügen einer Zeile für catdas Verfolgen einfacher wird, gibt es andere Möglichkeiten, Ihren Code zu bereinigen. Eines, das ich häufig verwende, um Skripte später leichter herauszufinden, ist das Aufteilen von Pipes in logische Mengen und das Speichern in Funktionen. Der Skriptcode wird dann sehr natürlich und jeder Teil der Pipline ist leichter zu debuggen.

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

fix_blahs < data

Sie könnten dann fortfahren fix_blahs < data | fix_frogs | reorder | format_for_sql. Eine Pipleline, die so liest, ist wirklich einfach zu befolgen, und die einzelnen Komponenten können problemlos in ihren jeweiligen Funktionen debuggt werden.


26
Ich wusste nicht, dass <filedas vor dem Befehl kommen könnte. Dies löst alle meine Probleme!

3
@ Tim: Bash und Zsh unterstützen das beide, obwohl ich es für hässlich halte. Wenn ich mir Sorgen mache, dass mein Code hübsch und wartbar ist, verwende ich normalerweise Funktionen, um ihn zu bereinigen. Siehe meine letzte Bearbeitung.
Caleb

8
@Tim <filekann überall in der Befehlszeile stehen: <file grep needleoder grep <file needleoder grep needle <file. Die Ausnahme bilden komplexe Befehle wie Schleifen und Gruppierungen. dort muss die umleitung nach dem schließen kommen done/ }/ )/ etc. @Caleb Dies gilt für alle Bourne / POSIX-Shells. Und ich stimme nicht zu, dass es hässlich ist.
Gilles 'SO- hör auf böse zu sein'

9
@Gilles, in der Bash können Sie ersetzen $(cat /some/file)mit $(< /some/file), was das Gleiche tut , vermeidet aber einen Prozess Laichen.
cjm

3
Nur um zu bestätigen, dass die $(< /some/file)Portabilität eingeschränkt ist. Es funktioniert in bash, aber nicht in BusyBox ash oder FreeBSD sh. Funktioniert wahrscheinlich auch nicht im Armaturenbrett, da die letzten drei Muscheln alle enge Verwandte sind.
dubiousjim

22

Hier ist eine Zusammenfassung einiger der Nachteile von:

cat $file | cmd

Über

< $file cmd
  • Zunächst ein Hinweis: Es fehlen (absichtlich zum Zwecke der Diskussion) oben doppelte Anführungszeichen $file. Im Falle von catist das immer ein Problem, mit Ausnahme von zsh; Bei der Umleitung ist dies nur ein Problem für bashoder ksh88, bei einigen anderen Shells nur dann, wenn sie interaktiv sind (nicht in Skripten).
  • Der am häufigsten genannte Nachteil ist der zusätzliche Prozess, der erzeugt wird. Beachten Sie, dass, wenn cmdes eingebaut ist, dies in einigen Shells sogar 2 Prozesse sind bash.
  • Immer noch auf der Performance-Front, außer in Shells, in denen cates eingebaut ist, wird auch ein zusätzlicher Befehl ausgeführt (und natürlich geladen und initialisiert (und auch die Bibliotheken, mit denen er verknüpft ist)).
  • Bei großen Dateien bedeutet dies, dass das System die catund cmd-Prozesse abwechselnd einplanen und den Pipe-Puffer ständig auffüllen und leeren muss. Auch wenn cmdtut 1GBgroße read()Systemaufrufe zu einer Zeit, wird die Steuerung muß hin und her zwischen gehen catund cmdweil ein Rohr kann nicht mehr als ein paar Kilobyte Daten zu einer Zeit halten.
  • Einige cmds (wie wc -c) können einige Optimierungen vornehmen, wenn ihr stdin eine reguläre Datei ist, mit der sie nichts cat | cmdanfangen können, da ihr stdin dann nur eine Pipe ist. Mit catund einer Pipe bedeutet dies auch, dass sie nicht seek()in der Datei enthalten sein können. Bei Befehlen wie tacoder tailmacht dies einen großen Unterschied in der Leistung, da bei diesen Befehlen catdie gesamte Eingabe im Speicher abgelegt werden muss.
  • Die cat $fileund sogar die korrektere Version cat -- "$file"funktionieren nicht richtig für bestimmte Dateinamen wie -( --helpoder alles, was mit beginnt, -wenn Sie die vergessen --). Wenn man darauf besteht cat, sollte man aus cat < "$file" | cmdGründen der Zuverlässigkeit wahrscheinlich stattdessen verwenden.
  • Wenn $filees nicht zum Lesen geöffnet werden kann (Zugriff verweigert, existiert nicht ...), < "$file" cmdwird eine konsistente Fehlermeldung (von der Shell) gemeldet und nicht ausgeführt cmd, während cat $file | cmdes weiterhin ausgeführt wird cmd, wobei die Standard-ID wie eine leere Datei aussieht. Das bedeutet auch, dass in Sachen < file cmd > file2, file2wenn nicht geöffnet werden filekann , nicht überladen wird .

2
In Bezug auf die Leistung: Dieser Test zeigt, dass der Unterschied in der Größenordnung von 1 Prozent liegt, es sei denn, Sie verarbeiten nur sehr wenig im Stream oletange.blogspot.dk/2013/10/useless-use-of-cat.html
Ole Tange

2
@OleTange. Hier ist ein weiterer Test: truncate -s10G a; time wc -c < a; time cat a | wc -c; time cat a | cat | wc -c. Es gibt viele Parameter, die ins Bild kommen. Die Performance-Einbußen können von 0 bis 100% gehen. Ich denke jedenfalls nicht, dass die Strafe negativ sein kann.
Stéphane Chazelas

2
wc -cist ein ziemlich einzigartiger Fall, weil es eine Verknüpfung hat. Wenn Sie stattdessen tut , wc -wdann ist es vergleichbar grepin meinem Beispiel (dh sehr wenig Verarbeitung - das ist die Situation , wo ‚<‘ kann einen Unterschied machen).
Ole Tange

@OleTange, sogar ( wc -wauf einer 1GB-Sparse-Datei im C-Gebietsschema unter Linux 4.9 und 64), dann finde ich, dass der cat-Ansatz 23% mehr Zeit in einem Multicore-System und 5% mehr Zeit in einem Core benötigt. Zeigt den zusätzlichen Aufwand an, der entsteht, wenn auf Daten von mehr als einem Kern zugegriffen wird. Sie erhalten möglicherweise unterschiedliche Ergebnisse, wenn Sie die Größe der Pipe ändern, unterschiedliche Daten verwenden, echte E / A-Vorgänge verwenden und splice () verwenden. Dies alles bestätigt, dass viele Parameter im Bild enthalten sind und das wird auf jeden catfall nicht helfen.
Stéphane Chazelas

1
Bei einer 1-GB-Datei beträgt wc -wder Unterschied ungefähr 2% ... 15%, wenn es sich um ein einfaches Grep handelt. Dann, seltsamerweise, wenn es auf einer NFS-Dateifreigabe ist, ist es tatsächlich 20% schneller zu lesen, wenn es von cat( gist.github.com/rdp/7162414833becbee5919cda855f1cb86 ) Weird ...
Rogerdpack

16

Das Platzieren <fileam Ende einer Pipeline ist weniger lesbar als cat fileam Anfang. Natürliches Englisch liest von links nach rechts.

Den <fileStart der Pipeline zu setzen ist auch weniger lesbar als cat, würde ich sagen. Ein Wort ist besser lesbar als ein Symbol, insbesondere ein Symbol, das in die falsche Richtung weist.

Mit wird catdas command | command | commandFormat beibehalten .


Ich bin damit einverstanden, dass die Verwendung von <Once den Code weniger lesbar macht, da dadurch die Syntaxkonsistenz einer Multipipeline zerstört wird.
A.Danischewski

@Jim Sie können die Lesbarkeit lösen, indem Sie einen Alias <wie diesen erstellen : alias load='<'und dann z load file | sed .... Aliase können nach der Ausführung in Skripten verwendet werden shopt -s expand_aliases.
Niiieani

1
Ja, ich kenne Aliase. Obwohl dieser Alias ​​das Symbol durch ein Wort ersetzt, muss der Leser Ihre persönlichen Alias-Einstellungen kennen, sodass er nicht sehr portabel ist.
Jim

8

Eine Sache, die die anderen Antworten hier offenbar nicht direkt angesprochen haben, ist, dass eine solche Verwendung catnicht "nutzlos" ist in dem Sinne, dass "ein irrelevanter Katzenprozess entsteht, der keine Arbeit leistet". Es ist in dem Sinne nutzlos, dass "ein Katzenprozess erzeugt wird, der nur unnötige Arbeit leistet".

Im Fall dieser beiden:

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

Die Shell startet einen sed-Prozess, der von somefile oder stdin (bzw.) liest, und führt dann eine Verarbeitung durch - sie liest, bis sie eine neue Zeile erreicht, ersetzt das erste 'foo' (falls vorhanden) in dieser Zeile durch 'bar' und druckt dann diese Linie zu stdout und Schleifen.

Im Falle des:

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

Die Muschel erzeugt einen Katzenprozess und einen Sed-Prozess und verdrahtet den Standard der Katze mit dem Standard der Sed. Der cat-Prozess liest einen Teil von mehreren Kilo- oder vielleicht Megabyte aus der Datei und schreibt ihn dann in seine Standardausgabe, in der der sed-Befehl wie im obigen zweiten Beispiel von dort abhebt. Während sed diesen Chunk verarbeitet, liest cat einen weiteren Chunk und schreibt ihn in seine Standardausgabe, damit sed als nächstes daran arbeiten kann.

Mit anderen Worten, die zusätzliche Arbeit, die durch das Hinzufügen des catBefehls erforderlich ist, ist nicht nur die zusätzliche Arbeit, einen zusätzlichen catProzess zu erzeugen , sondern auch die zusätzliche Arbeit, die Bytes der Datei zweimal statt einmal zu lesen und zu schreiben. In der Praxis und auf modernen Systemen macht dies keinen großen Unterschied - es kann dazu führen, dass Ihr System einige Mikrosekunden unnötiger Arbeit leistet. Wenn es sich jedoch um ein Skript handelt, das Sie verteilen möchten, möglicherweise an Personen, die es auf Computern verwenden, die bereits nicht ausreichend ausgelastet sind, können sich einige Mikrosekunden über viele Iterationen summieren.


2
In oletange.blogspot.dk/2013/10/useless-use-of-cat.html finden Sie einen Test des Overheads für die Verwendung der zusätzlichen cat.
Ole Tange

@OleTange: Ich bin gerade darauf gestoßen und habe Ihren Blog besucht. (1) Während ich den Inhalt (meistens) auf Englisch sehe, sehe ich eine Reihe von Wörtern auf Dänisch: "Klassisk", "Flipcard", "Magasin", "Mosaik", "Sidebjælke", "Øjebliksbillede". , "Tidsskyder", "Blog-arkiv", "Om mig", "Skrevet" und "Vis kommentarer" (aber "Tweet", "Like" und das Cookies-Banner sind in englischer Sprache). Wusstest du davon und hast du es unter Kontrolle? (2) Ich habe Probleme beim Lesen Ihrer Tabellen (2a), weil die Gitternetzlinien unvollständig sind, und (2b) ich verstehe nicht, was Sie mit "Diff (pct)" meinen.
G-Man sagt, dass Monica am

blogspot.dk wird von Google betrieben. Versuchen Sie, durch blogspot.com zu ersetzen. Der "Diff (pct)" ist die ms mit catgeteilt durch die ms ohne catin Prozent (zB 264 ms / 216 ms = 1.22 = 122% = 22% langsamer mit cat)
Ole Tange
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.