Warum repariert Shell nicht automatisch den „unnützen Gebrauch von Katze“? [geschlossen]


28

Viele Leute verwenden Oneliners und Skripte, die Code nach dem Vorbild enthalten

cat "$MYFILE" | command1 | command2 > "$OUTPUT"

Der erste catwird oft als "nutzloser Einsatz von Katze" bezeichnet, da technisch (oft /usr/bin/cat) ein neuer Prozess gestartet werden muss, bei dem dies vermieden werden könnte, wenn der Befehl ausgeführt worden wäre

< "$MYFILE" command1 | command2 > "$OUTPUT"

Denn dann muss die Shell nur noch starten command1und stdinauf die angegebene Datei verweisen .

Warum führt die Shell diese Konvertierung nicht automatisch durch? Ich bin der Meinung, dass die Syntax "unnütze Verwendung von cat" einfacher zu lesen ist und Shell über genügend Informationen verfügen sollte, um unnütze cat automatisch loszuwerden. Das catist im POSIX-Standard definiert, daher sollte die Shell die Möglichkeit haben, es intern zu implementieren, anstatt einen binären Pfad zu verwenden. Die Shell könnte sogar nur eine Implementierung für genau eine Argumentversion enthalten und auf binären Pfad zurückgreifen.


22
Diese Befehle sind nicht gleichbedeutend, da stdin in einem Fall eine Datei und in dem anderen Fall eine Pipe ist, sodass es sich nicht um eine streng sichere Konvertierung handelt. Sie könnten jedoch ein System entwickeln, das dies tut.
Michael Homer

14
Dass Sie sich einen Anwendungsfall nicht vorstellen können, bedeutet nicht, dass sich eine Anwendung nicht nutzlos auf das angegebene Verhalten verlassen darf. Immer ein Fehler von lseeknoch Verhalten definiert und könnte zu einem anderen Ergebnis führt, kann das unterschiedliche Sperrverhalten semantisch sinnvoll sein, usw. wäre es zulässig sein , um die Änderung zu machen , wenn Sie wissen , was die anderen Befehle waren und wussten , dass sie nicht egal, oder wenn Ihnen die Kompatibilität auf dieser Ebene einfach egal war, aber der Vorteil ist ziemlich gering. Ich kann mir vorstellen, dass der Mangel an Nutzen die Situation mehr antreibt als die Konformitätskosten.
Michael Homer

3
Die Shell darf sich jedoch catselbst oder ein anderes Dienstprogramm implementieren . Es ist auch zulässig zu wissen, wie die anderen zum System gehörenden Dienstprogramme funktionieren (z. B. wie sich die mit dem System gelieferte externe grepImplementierung verhält). Dies ist durchaus machbar, und es ist durchaus fair, sich zu fragen, warum dies nicht der Fall ist.
Michael Homer

6
@MichaelHomer zB kann es wissen, wie sich die mit dem System gelieferte externe grep-Implementierung verhält. Die Shell ist also jetzt vom Verhalten von abhängig grep. Und sed. Und awk. Und du. Und wie viele Hunderte, wenn nicht Tausende von anderen Versorgungsunternehmen?
Andrew Henle

19
Es wäre ziemlich unsinnig, meine Befehle für mich zu bearbeiten.
Azor Ahai

Antworten:


25

Die 2 Befehle sind nicht gleichbedeutend: Fehlerbehandlung beachten:

cat <file that doesn't exist> | less Es wird ein leerer Stream erzeugt, der an das Pipe-Programm übergeben wird. Als solches erhalten Sie eine Anzeige, die nichts anzeigt.

< <file that doesn't exist> less öffnet die Leiste nicht und öffnet dann überhaupt nicht weniger.

Der Versuch, das erste in das zweite zu ändern, kann eine beliebige Anzahl von Skripten beschädigen, die erwarten, dass das Programm mit einer möglicherweise leeren Eingabe ausgeführt wird.


1
Ich werde Ihre Antwort als akzeptiert markieren, da dies meiner Meinung nach der wichtigste Unterschied zwischen beiden Syntaxen ist. Die Variante mit catführt immer den zweiten Befehl in der Pipeline aus, während die Variante mit nur Eingabeumleitung den Befehl überhaupt nicht ausführt, wenn die Eingabedatei fehlt.
Mikko Rantalainen

Beachten Sie jedoch, dass <"missing-file" grep foo | echo 2nicht ausgeführt grepwird, sondern ausgeführt wird echo.
Mikko Rantalainen

51

"Useless use of cat" handelt mehr davon, wie Sie Ihren Code schreiben, als davon, was beim Ausführen des Skripts tatsächlich ausgeführt wird. Es ist eine Art Design- Anti-Pattern , eine Methode, mit der man wahrscheinlich effizienter vorgehen könnte. Es ist ein Fehler im Verständnis, wie die gegebenen Werkzeuge am besten kombiniert werden können, um ein neues Werkzeug zu erstellen. Ich würde argumentieren, dass das Aneinanderreihen von mehreren sedund / oder awkBefehlen in einer Pipeline auch manchmal als Symptom für dasselbe Anti-Pattern angesehen werden kann.

Das Beheben von Fällen von "unbrauchbarer Verwendung von cat" in einem Skript ist in erster Linie eine Sache des manuellen Behebens des Quellcodes des Skripts. Ein Tool wie ShellCheck kann dabei helfen, indem es auf die offensichtlichen Fälle hinweist:

$ cat script.sh
#!/bin/sh
cat file | cat
$ shellcheck script.sh

In script.sh line 2:
cat file | cat
    ^-- SC2002: Useless cat. Consider 'cmd < file | ..' or 'cmd file | ..' instead.

Die Shell dazu zu bringen, dies automatisch zu tun, wäre aufgrund der Art der Shell-Skripte schwierig. Die Art und Weise, wie ein Skript ausgeführt wird, hängt von der Umgebung ab, die von seinem übergeordneten Prozess geerbt wurde, und von der spezifischen Implementierung der verfügbaren externen Befehle.

Die Shell muss nicht unbedingt wissen, was catist. Es kann sich möglicherweise um einen Befehl von einer beliebigen Stelle in Ihrem System $PATHoder um eine Funktion handeln.

Wenn es ein eingebauter Befehl (die es in einigen Muscheln sein kann), es würde die Möglichkeit hat, die Pipeline neu zu organisieren , wie es die Semantik seiner integrierten in wissen würde catBefehl. Zuvor müsste zusätzlich über den nächsten Befehl in der Pipeline nach dem Original nachgedacht werden cat.

Beachten Sie, dass sich das Lesen von der Standardeingabe etwas anders verhält, wenn es mit einer Pipe verbunden ist und wenn es mit einer Datei verbunden ist. Eine Pipe kann nicht gesucht werden. Je nachdem, was der nächste Befehl in der Pipeline ausführt, kann sich die Pipeline möglicherweise anders verhalten oder auch nicht (es kann festgestellt werden, ob die Eingabe gesucht werden kann, und es kann entschieden werden, ob dies anders ist oder nicht) es ist nicht, auf jeden Fall würde es sich dann anders verhalten).

Diese Frage ist ähnlich (in einem sehr allgemeinen Sinn) auf „ Gibt es Compiler , die auf eigene Faust zu beheben Syntaxfehler versuchen? “ (Auf der Software Engineering Stack Ort), obwohl diese Frage offensichtlich über Syntaxfehler, nicht nutzlos Entwurfsmuster . Die Idee, den Code basierend auf der Absicht automatisch zu ändern, ist jedoch weitgehend dieselbe.


Es ist vollkommen konform, wenn eine Shell weiß, was catist, und die anderen Befehle in der Pipeline (die Als-ob-Regel) und sich dementsprechend verhalten, sie sind einfach nicht hier, weil es sinnlos und zu schwierig ist.
Michael Homer

4
@MichaelHomer Ja. Es ist aber auch erlaubt, einen Standardbefehl mit einer gleichnamigen Funktion zu überladen.
Kusalananda

2
@PhilipCouling Es ist absolut konform, solange bekannt ist, dass sich keiner der Pipeline-Befehle darum kümmert. Die Shell ist ausdrücklich berechtigt, Dienstprogramme durch eingebaute oder Shell-Funktionen zu ersetzen. Für diese gelten keine Einschränkungen für die Ausführungsumgebung. Solange das externe Ergebnis nicht unterscheidbar ist, ist dies zulässig. Für Ihren Fall cat /dev/ttyist das interessant, bei dem das anders wäre< .
Michael Homer

1
@MichaelHomer , solange das externe Ergebnis nicht unterscheidbar ist, ist es zulässig. Das bedeutet, dass sich das Verhalten aller auf diese Weise optimierten Dienstprogramme niemals ändern kann . Das muss die ultimative Abhängigkeitshölle sein.
Andrew Henle

3
@MichaelHomer Wie in den anderen Kommentaren bereits erwähnt, ist es für die Shell natürlich perfekt zu wissen, dass es angesichts der Eingaben des OP unmöglich ist, zu sagen, was der catBefehl tatsächlich tut, ohne ihn auszuführen . catSoweit Sie (und die Shell) wissen, hat das OP einen Befehl in ihrem Pfad, der eine interaktive Katzensimulation ist, "myfile" ist nur der gespeicherte Spielstatus command1und verarbeitet command2einige Statistiken über die aktuelle Spielsitzung nach ...
alephzero

34

Weil es nicht nutzlos ist.

In diesem Fall cat file | cmdist fd 0(stdin) von cmdeine Pipe, und in diesem Fall cmd <filekann es sich um eine reguläre Datei, ein Gerät usw. handeln.

Eine Pipe hat eine andere Semantik als eine reguläre Datei, und ihre Semantik ist keine Teilmenge derjenigen einer regulären Datei:

  • Eine reguläre Datei kann nicht auf sinnvolle Weise select(2)bearbeitet oder poll(2)bearbeitet werden. Ein select(2)darauf wird immer "ready" zurückgeben. Fortgeschrittene Benutzeroberflächen wie epoll(2)unter Linux funktionieren mit normalen Dateien einfach nicht.

  • auf Linux gibt es Systemaufrufe ( splice(2), vmsplice(2), tee(2)) , die nur Arbeiten an Rohrleitungen [1]

Da cates so häufig verwendet wird, kann es als integrierte Shell implementiert werden, wodurch ein zusätzlicher Prozess vermieden wird. Wenn Sie jedoch erst einmal auf diesem Pfad gestartet sind, können Sie mit den meisten Befehlen dasselbe tun - die Shell wird langsamer und klüger perloder python. es ist wahrscheinlich besser, eine andere Skriptsprache mit einer einfach zu verwendenden Pipe-ähnlichen Syntax für Fortsetzungen zu schreiben ;-)

[1] Wenn Sie ein einfaches Beispiel haben möchten, das für diesen Anlass nicht erfunden wurde , können Sie sich mein "exec binary from stdin" -Git mit einigen Erläuterungen im Kommentar hier ansehen . Das Implementieren catin das Programm, um es ohne UUoC arbeiten zu lassen, hätte es zwei- oder dreimal größer gemacht.


2
In der Tat, ksh93 funktioniert wie einige externe Befehle implementieren catintern.
jrw32982 unterstützt Monica

3
cat /dev/urandom | cpu_bound_programführt die read()Systemaufrufe in einem separaten Prozess aus. Unter Linux wird beispielsweise die eigentliche CPU-Arbeit zum Generieren von mehr Zufallszahlen (wenn der Pool leer ist) in diesem Systemaufruf erledigt. Wenn Sie also einen separaten Prozess verwenden, können Sie einen separaten CPU-Kern verwenden, um Zufallsdaten als Eingabe zu generieren. zB in Was ist der schnellste Weg, um eine 1 GB große Textdatei mit zufälligen Ziffern zu generieren?
Peter Cordes

4
Noch wichtiger ist, dass dies in den meisten Fällen lseeknicht funktioniert. cat foo.mp4 | mpv -funktioniert, aber Sie können nicht weiter rückwärts suchen als den Cache-Puffer von mpv oder mplayer. Aber mit Eingaben aus einer Datei umgeleitet, können Sie. cat | mpv -Dies ist eine Möglichkeit zu überprüfen, ob ein MP4 sein moovAtom am Anfang der Datei hat, damit es abgespielt werden kann, ohne bis zum Ende und zurück zu suchen (dh ob es für das Streamen geeignet ist). Es ist leicht vorstellbar, dass Sie ein Programm in anderen Fällen auf nicht durchsuchbare Dateien testen möchten, indem Sie es /dev/stdinmit cateiner Umleitung ausführen .
Peter Cordes

Dies gilt umso mehr bei der Verwendung xargs cat | somecmd. Wenn sich die Dateipfade über das Befehlspufferlimit hinaus erstrecken, xargskönnen sie catmehrmals ausgeführt werden, was zu einem kontinuierlichen Datenstrom führt. Die xargs somecmddirekte Verwendung schlägt jedoch häufig fehl, da somecmdsie nicht in mehreren Schritten ausgeführt werden kann, um ein nahtloses Ergebnis zu erzielen.
Tasket

17

Weil es wirklich sehr schwer ist, nutzlose Katzen zu entdecken.

Ich hatte ein Shell-Skript, in dem ich schrieb

cat | (somecommand <<!
...
/proc/self/fd/3
...
!) 0<&3

Das Shell-Skript schlug in der Produktion fehl, wenn das catentfernt wurde, weil es über aufgerufen wurde su -c 'script.sh' someuser. Das anscheinend Überflüssige catveranlasste den Besitzer der Standardeingabe, den Benutzer zu ändern, als der das Skript ausgeführt wurde, so dass das erneute Öffnen über /procfunktionierte.


Dieser Fall wäre ziemlich einfach, da er eindeutig nicht dem einfachen Modell von catgefolgt von genau einem Parameter folgt, sodass die Shell eine echte catausführbare Datei anstelle einer optimierten Verknüpfung verwenden sollte. Guter Punkt für möglicherweise unterschiedliche Anmeldeinformationen oder nicht standardmäßige Standardanforderungen für echte Prozesse.
Mikko Rantalainen

13

tl; dr: Shells machen das nicht automatisch, weil die Kosten die wahrscheinlichen Vorteile übersteigen.

Andere Antworten haben den technischen Unterschied zwischen stdin als Pipe und stdin als Datei aufgezeigt. Unter Berücksichtigung dessen könnte die Shell eine der folgenden Aktionen ausführen:

  1. Implementieren Sie es catals integriertes Element, wobei die Unterscheidung zwischen Datei und Pipe erhalten bleibt. Dies würde die Kosten einer Führungskraft und möglicherweise einer Gabel sparen.
  2. Führen Sie eine vollständige Analyse der Pipeline durch, und kennen Sie die verschiedenen Befehle, die verwendet werden, um festzustellen, ob Datei- / Pipe-Probleme auftreten, und handeln Sie danach.

Als nächstes müssen Sie die Kosten und den Nutzen jedes Ansatzes berücksichtigen. Die Vorteile sind einfach genug:

  1. Vermeiden Sie in jedem Fall ein exec (von cat)
  2. Im zweiten Fall, wenn ein Redirect-Austausch möglich ist, kann auf eine Gabel verzichtet werden.
  3. In Fällen , in denen Sie ein Rohr verwenden haben, ist es vielleicht möglich , manchmal sein , eine Gabel / vfork zu vermeiden, aber oft nicht. Dies liegt daran, dass das Cat-Äquivalent gleichzeitig mit dem Rest der Pipeline ausgeführt werden muss.

Sie sparen also ein wenig CPU-Zeit und Arbeitsspeicher, vor allem, wenn Sie die Gabel vermeiden können. Natürlich sparen Sie diese Zeit und den Speicher nur, wenn die Funktion tatsächlich verwendet wird. Und Sie sparen nur die Fork / Exec-Zeit; Bei größeren Dateien ist die Zeit meist die E / A-Zeit (dh Katze liest eine Datei von der Festplatte). Sie müssen sich also fragen: Wie oft wird cat(sinnlos) in Shell-Skripten verwendet, bei denen die Leistung tatsächlich zählt? Vergleichen Sie es mit anderen gebräuchlichen Shell-Buildins wie test- es ist schwer vorstellbar, dass catsie (sinnlos) sogar ein Zehntel so oft testverwendet werden wie an wichtigen Orten. Das ist eine Vermutung, die ich nicht gemessen habe. Das ist etwas, was Sie tun möchten, bevor Sie versuchen, es umzusetzen. (Oder in ähnlicher Weise jemanden bitten, eine Funktionsanforderung zu implementieren.)

Als nächstes fragen Sie: Was sind die Kosten. Die beiden Kosten, die in den Sinn kommen, sind: (a) zusätzlicher Code in der Shell, der größer wird (und somit möglicherweise mehr Speicher benötigt), mehr Wartungsarbeiten erfordert, ein weiterer Punkt für Fehler ist usw .; und (b) Überraschungen in Bezug catauf die Abwärtskompatibilität, POSIX lässt viele Funktionen von z. B. GNU-Coreutils aus cat, sodass Sie genau darauf achten müssen, was das cateingebaute Programm implementieren würde.

  1. Die zusätzliche eingebaute Option ist wahrscheinlich nicht so schlimm - wenn Sie eine weitere eingebaute Option hinzufügen, bei der bereits eine Menge vorhanden ist. Wenn Profildaten hilfreich wären, könnten Sie wahrscheinlich die Autoren Ihrer Lieblingsshell davon überzeugen, sie hinzuzufügen.

  2. Was die Analyse der Pipeline angeht, glaube ich nicht, dass Shells derzeit so etwas tun (einige erkennen das Ende einer Pipeline und können eine Gabelung vermeiden). Im Wesentlichen würden Sie der Shell einen (primitiven) Optimierer hinzufügen. Optimierer erweisen sich oft als komplizierter Code und die Quelle vieler Fehler. Und diese Fehler können überraschend sein - leichte Änderungen im Shell-Skript könnten den Fehler vermeiden oder auslösen.

Nachtrag: Sie können eine ähnliche Analyse auf Ihre nutzlosen Verwendungen von cat anwenden. Vorteile: Einfacher zu lesen (obwohl befehl1 wahrscheinlich keine Datei als Argument verwendet). Kosten: extra fork und exec (und wenn command1 eine Datei als Argument nehmen kann, wahrscheinlich verwirrendere Fehlermeldungen). Wenn Ihre Analyse zeigt, dass Sie cat sinnlos verwenden sollen, fahren Sie fort.


10

Der catBefehl kann -als Marker für stdin akzeptiert werden . ( POSIX : " Wenn eine Datei '-' ist, muss das cat-Dienstprogramm an diesem Punkt in der Sequenz von der Standardeingabe lesen. ") Dies ermöglicht die einfache Behandlung einer Datei oder eines stdin, wo dies andernfalls nicht zulässig wäre.

Betrachten Sie diese beiden einfachen Alternativen, bei denen das Shell-Argument $1lautet -:

cat "$1" | nl    # Works completely transparently
nl < "$1"        # Fails with 'bash: -: No such file or directory'

Eine andere Zeit catist nützlich, wenn es absichtlich als No-Op verwendet wird, um die Shell-Syntax beizubehalten:

file="$1"
reader=cat
[[ $file =~ \.gz$ ]] && reader=zcat
[[ $file =~ \.bz2$ ]] && reader=bzcat
"$reader" "$file"

Schließlich glaube ich, dass das einzige Mal, dass UUOC wirklich korrekt aufgerufen werden kann, die catVerwendung eines Dateinamens ist, von dem bekannt ist, dass er eine reguläre Datei ist (dh kein Gerät oder keine Named Pipe), und dass dem Befehl keine Flags gegeben werden:

cat file.txt

In jeder anderen Situation können die Eigenheiten caterforderlich sein.


6

Der Befehl cat kann Dinge ausführen, die die Shell nicht unbedingt ausführen kann (oder zumindest nicht leicht ausführen kann). Angenommen, Sie möchten Zeichen drucken, die ansonsten möglicherweise unsichtbar sind, z. B. Tabulatoren, Zeilenumbrüche oder Zeilenumbrüche. Es * könnte * eine Möglichkeit geben, dies nur mit Shell-Befehlen zu tun, aber ich kann mir keine aus dem Kopf denken. Die GNU-Version von cat kann dies mit dem -AArgument oder den -v -E -TArgumenten tun (ich weiß jedoch nichts über andere Versionen von cat). Sie können auch jeder Zeile eine Zeilennummer voranstellen -n(erneut IDK, wenn dies in Nicht-GNU-Versionen möglich ist).

Ein weiterer Vorteil von cat ist, dass es problemlos mehrere Dateien lesen kann. Dazu kann man einfach tippen cat file1 file2 file3. Dasselbe mit einer Shell zu tun, würde schwierig werden, obwohl eine sorgfältig ausgearbeitete Schleife höchstwahrscheinlich das gleiche Ergebnis erzielen könnte. Wollen Sie sich wirklich die Zeit nehmen, um eine solche Schleife zu schreiben, wenn es eine so einfache Alternative gibt? Ich nicht!

Das Lesen von Dateien mit cat würde wahrscheinlich weniger CPU verbrauchen als die Shell, da cat ein vorkompiliertes Programm ist (die offensichtliche Ausnahme ist jede Shell, die eine eingebaute cat hat). Wenn ich eine große Gruppe von Dateien lese, wird dies möglicherweise deutlich, aber ich habe dies auf meinen Computern noch nie getan, daher kann ich nicht sicher sein.

Der Befehl cat kann auch nützlich sein, um einen Befehl zu zwingen, Standardeingaben zu akzeptieren, wenn dies nicht der Fall ist. Folgendes berücksichtigen:

echo 8 | sleep

Die Zahl "8" wird vom Befehl "sleep" nicht akzeptiert, da sie eigentlich keine Standardeingabe akzeptieren sollte. Daher wird der Schlaf diese Eingabe ignorieren, sich über einen Mangel an Argumenten beschweren und beenden. Wenn man jedoch Folgendes eingibt:

echo 8 | sleep $(cat)

Viele Muscheln erweitern dies auf sleep 8und der Schlaf wartet 8 Sekunden, bevor er beendet wird. Sie können mit ssh auch etwas Ähnliches tun:

command | ssh 1.2.3.4 'cat >> example-file'

Dieser Befehl mit angehängter Beispieldatei auf der Maschine mit der Adresse 1.2.3.4 mit allem, was von "Befehl" ausgegeben wird.

Und das kratzt (wahrscheinlich) nur an der Oberfläche. Ich bin sicher, ich könnte mehr Beispiele dafür finden, dass Katze nützlich ist, wenn ich wollte, aber dieser Beitrag ist lang genug, so wie er ist. Abschließend sage ich Folgendes: Es ist nicht wirklich machbar, die Shell aufzufordern, alle diese (und einige andere) Szenarien zu antizipieren.


Ich würde den letzten Satz mit "ist nicht leicht durchführbar"
beenden

3

Denken Sie daran , dass ein Benutzer ein hätte catin seinen $PATHdie ist nicht genau das POSIX cat(aber vielleicht einige Varianten , die etwas irgendwo einloggen konnte). In diesem Fall soll die Shell sie nicht entfernen.

Das PATH könnte sich dynamisch ändern und ist dann cat nicht das, was du glaubst. Es wäre ziemlich schwierig, eine Shell zu schreiben, die die Optimierung ausführt, von der Sie träumen.

Auch in der Praxis cat ist das ein recht schnelles Programm. Es gibt wenige praktische Gründe (mit Ausnahme der Ästhetik), um dies zu vermeiden.

Siehe auch das ausgezeichnete Parsing POSIX [s] Höllengespräch von Yann Regis-Gianas auf der FOSDEM2018. Es gibt andere gute Gründe, nicht zu versuchen, das zu tun, wovon Sie in einer Shell träumen.

Wenn die Leistung wirklich ein Problem für Shells wäre, hätte jemand eine Shell vorgeschlagen, die eine ausgeklügelte Optimierung des gesamten Programm-Compilers, statische Quellcode-Analyse und Just-in-Time-Kompilierungstechniken verwendet (alle diese drei Bereiche sind jahrzehntelang fortgeschritten und für wissenschaftliche Veröffentlichungen bestimmt) Konferenzen, zB unter SIGPLAN ). Leider wird dieses interessante Forschungsthema derzeit nicht von Forschungsagenturen oder Risikokapitalgebern finanziert, und ich schließe daraus, dass es sich einfach nicht lohnt. Mit anderen Worten, es gibt wahrscheinlich keinen nennenswerten Markt für die Optimierung von Schalen . Wenn Sie eine halbe Million Euro für solche Forschungen ausgeben müssen, finden Sie leicht jemanden, der dies tut, und ich glaube, dies würde lohnende Ergebnisse bringen.

In der Praxis wird häufig ein kleines (einhundert Zeilen) Shell-Skript in einer besseren Skriptsprache (Python, AWK, Guile, ...) umgeschrieben, um die Leistung zu verbessern. Und es ist (aus vielen Gründen der Softwareentwicklung) nicht sinnvoll, große Shell-Skripte zu schreiben: Wenn Sie ein Shell-Skript mit mehr als hundert Zeilen schreiben, müssen Sie es (auch aus Gründen der Lesbarkeit und Wartung) in einer geeigneteren Sprache neu schreiben : Als Programmiersprache ist die Shell eine sehr schlechte. Es gibt jedoch viele große generierte Shell-Skripte, und das aus guten Gründen (z. B. GNU autoconf generierte configureSkripte).

Bei großen Textdateien ist es keine gute Praxis, sie catals einziges Argument zu übergeben, und die meisten Systemadministratoren wissen, dass (wenn die Ausführung eines Shell-Skripts länger als eine Minute dauert, sollten Sie überlegen, es zu optimieren). Bei großen Gigabyte-Dateien catist dies nie das richtige Werkzeug, um sie zu verarbeiten.


3
"Nicht wenige praktische Gründe, dies zu vermeiden" - jeder, der darauf gewartet hat, cat some-huge-log | tail -n 5zu rennen (wo tail -n 5 some-huge-logdirekt zum Ende gesprungen werden könnte, während catnur von vorne nach hinten gelesen wird), würde dem nicht zustimmen.
Charles Duffy

Das catAuschecken einer großen Textdatei im Dutzend-GB-Bereich (die zum Testen erstellt wurde) dauert ziemlich lange. Würde es nicht empfehlen.
Sergiy Kolodyazhnyy

1
Übrigens: "Kein bedeutender Markt für die Optimierung von Shells" - ksh93 ist eine optimierende und recht gute Shell. Es wurde für eine Weile erfolgreich als kommerzielles Produkt verkauft. (Leider hat die kommerzielle Lizenzierung auch dazu geführt, dass schlecht geschriebene Klone und andere weniger leistungsfähige, aber kostenlose Nachfolger die Welt außerhalb derjenigen Standorte eroberten, die bereit waren, für eine Lizenz zu zahlen, was zu der Situation führte, die wir hatten haben heute).
Charles Duffy

(Nicht die spezifischen Techniken, die Sie notieren, verwenden, aber ehrlich gesagt, machen diese Techniken angesichts des Prozessmodells keinen Sinn . Die Techniken, die sie anwenden, sind gut, gut angewendet und haben einen guten Effekt. )
Charles Duffy

2

In der @ Kusalananda-Antwort (und dem @alephzero-Kommentar) kann cat alles sein:

alias cat='gcc -c'
cat "$MYFILE" | command1 | command2 > "$OUTPUT"

oder

echo 'echo 1' > /usr/bin/cat
cat "$MYFILE" | command1 | command2 > "$OUTPUT"

Es gibt keinen Grund, warum cat (für sich allein) oder / usr / bin / cat im System tatsächlich cat ist, das Verkettungstool.


3
Anders als das Verhalten von catwird von POSIX definiert und sollte daher nicht wild anders sein.
Roaima

2
@roaima: PATH=/home/Joshua/bin:$PATH cat ...Bist du sicher, dass du weißt, was catjetzt passiert?
Joshua

1
@Joshua es ist nicht wirklich wichtig. Wir wissen beide, catdass sie überschrieben werden können, aber wir wissen auch, dass sie nicht mutwillig durch etwas anderes ersetzt werden sollten. Mein Kommentar weist darauf hin, dass POSIX eine bestimmte (Teil-) Verhaltensweise vorschreibt, deren Existenz vernünftigerweise erwartet werden kann. Manchmal habe ich ein Shell-Skript geschrieben, das das Verhalten eines Standarddienstprogramms erweitert. In diesem Fall verhielt sich das Shell-Skript genauso wie das ersetzte Tool, nur dass es zusätzliche Funktionen hatte.
Roaima

@Joshua: Auf den meisten Plattformen wissen (oder wissen könnten ) Shells, welche Verzeichnisse ausführbare Dateien enthalten, die POSIX-Befehle implementieren. Sie können die Ersetzung also einfach bis nach der Alias-Erweiterung und der Pfadauflösung verschieben und dies nur für ausführen /bin/cat. (Und Sie würden es zu einer Option machen, die Sie deaktivieren könnten.) Oder Sie würden cateine eingebaute Shell machen (auf die möglicherweise /bin/catfür mehrere Argumente zurückgegriffen wird?), Damit Benutzer steuern können, ob sie die externe Version normal verwenden möchten oder nicht Weise mit enable cat. Wie für kill. (Ich dachte, dass bash command catfunktionieren würde, aber das überspringt keine eingebauten)
Peter Cordes

Wenn Sie einen Alias ​​angeben, weiß die Shell, dass catin dieser Umgebung nicht mehr auf das Übliche verwiesen wird cat. Die Optimierung sollte natürlich erst nach der Verarbeitung der Aliase durchgeführt werden. Ich betrachte Shell-Built-Ins als Darstellung von Befehlen in virtuellen Verzeichnissen, die immer vor Ihrem Pfad stehen. Wenn Sie die Shell-Version eines Befehls vermeiden möchten (z. B. test), müssen Sie eine Variante mit einem Pfad verwenden.
Mikko Rantalainen

1

Zwei "nutzlose" Anwendungen für Katze:

sort file.txt | cat header.txt - footer.txt | less

... hier catwerden Datei- und Pipe-Eingang gemischt.

find . -name '*.info' -type f | sh -c 'xargs cat' | sort

... hier xargskann eine praktisch unbegrenzte Anzahl von Dateinamen akzeptiert und catso oft wie nötig ausgeführt werden, während sich alles wie ein Stream verhält. Dies funktioniert also für große Dateilisten, bei denen eine direkte Verwendung xargs sortnicht möglich ist.


Diese beiden Anwendungsfälle lassen sich trivial vermeiden, indem die Shell nur dann eingebettet wird, wenn catsie mit genau einem Argument aufgerufen wird. Insbesondere in dem Fall, dass shein String übergeben und direkt xargsaufgerufen catwird, kann die Shell die integrierte Implementierung nicht verwenden.
Mikko Rantalainen

0

Abgesehen von anderen Dingen catwürde -check zusätzlichen Performance-Overhead und Verwirrung darüber verursachen, welche Verwendung cattatsächlich nutzlos ist, IMHO, da solche Überprüfungen ineffizient sein und Probleme mit der legitimen catVerwendung verursachen können.

Wenn Befehle mit den Standard-Streams arbeiten, müssen sie sich nur um das Lesen / Schreiben der Standard-Dateideskriptoren kümmern. Befehle können wissen, ob stdin suchbar / suchbar ist oder nicht, was auf eine Pipe oder Datei hinweist.

Wenn wir dem Mix hinzufügen und prüfen, welcher Prozess tatsächlich diesen Standardinhalt liefert, müssen wir den Prozess auf der anderen Seite der Leitung finden und die entsprechende Optimierung anwenden. Dies kann in Bezug auf die Shell selbst erfolgen, wie im SuperUser- Post von Kyle Jones gezeigt, und in Bezug auf die Shell, die dies ist

(find /proc -type l | xargs ls -l | fgrep 'pipe:[20043922]') 2>/dev/null

wie im verlinkten Beitrag gezeigt. Dies sind 3 weitere Befehle (also zusätzliche fork()s und exec()s) und rekursive Durchläufe (also eine ganze Menge von readdir()Aufrufen).

In Bezug auf C und den Shell-Quellcode kennt die Shell den untergeordneten Prozess bereits, sodass keine Rekursion erforderlich ist. Aber woher wissen wir, wann optimiert werden muss und wann cattatsächlich nutzlos ist? Es gibt in der Tat nützliche Verwendungen von Katzen , wie z

# adding header and footer to file
( cmd; cat file; cmd ) | cmd
# tr command does not accept files as arguments
cat log1 log2 log3 | tr '[:upper:]' '[:lower:]'

Es wäre wahrscheinlich eine Verschwendung und unnötiger Aufwand, der Shell eine solche Optimierung hinzuzufügen. Wie in Kusalandas Antwort bereits erwähnt, handelt es sich bei UUOC eher um das eigene Unverständnis des Benutzers, wie Befehle am besten kombiniert werden können, um die besten Ergebnisse zu erzielen.

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.