Warum sind in der Shell keine obligatorischen POSIX-Dienstprogramme integriert?

45

Der Zweck dieser Frage ist es, eine Neugierde zu beantworten und nicht ein bestimmtes Computerproblem zu lösen. Die Frage ist: Warum sind obligatorische POSIX-Dienstprogramme nicht allgemein in Shell-Implementierungen integriert?

Ich habe zum Beispiel ein Skript, das im Grunde genommen ein paar kleine Textdateien liest und überprüft, ob sie richtig formatiert sind, aber es dauert 27 Sekunden, bis sie auf meinem Computer ausgeführt werden, da ein erheblicher Teil der Zeichenfolgen bearbeitet wird. Diese String-Manipulation bringt Tausende neuer Prozesse hervor, indem verschiedene Dienstprogramme aufgerufen werden, daher die Langsamkeit. Ich bin mir ziemlich sicher , dass , wenn einige der Dienstprogramme in gebaut wurden, nämlich grep, sed, cut, tr, und exprdann das Skript in einem zweiten laufen würde oder weniger (basierend auf meiner Erfahrung in C).

Es scheint viele Situationen zu geben, in denen der Einbau dieser Dienstprogramme den Unterschied zwischen einer akzeptablen Leistung einer Lösung in Shell-Skripten ausmacht.

Offensichtlich gibt es einen Grund, warum diese Dienstprogramme nicht integriert wurden. Wenn Sie möglicherweise eine Version eines Dienstprogramms auf Systemebene verwenden, wird vermieden, dass mehrere ungleiche Versionen dieses Dienstprogramms von verschiedenen Shells verwendet werden. Ich kann mir wirklich nicht viele andere Gründe vorstellen, um den Aufwand für die Erstellung so vieler neuer Prozesse zu verringern, und POSIX definiert die Dienstprogramme so genau, dass es kein großes Problem zu sein scheint, unterschiedliche Implementierungen zu haben, solange es sich um POSIX handelt konforme. Zumindest kein so großes Problem wie die Ineffizienz so vieler Prozesse.

Kyle
quelle
15
Wenn 27 Sekunden zu langsam sind, können Sie Python, Perl oder eine andere halbkompilierte Sprache verwenden. Alternativ können Sie die langsamen Teile Ihres Skripts posten und nach Verbesserungen fragen. Es kann sein, dass Sie drei oder vier Befehle verwenden, von denen einer (schneller) ausreicht.
Roaima
8
Shells waren leider nicht wirklich für schwere Aufgaben gemacht und die Welt hat sich sehr verändert, seit man mit nur einem Shell-Skript davonkommen konnte. Ich stimme Roaima zu - jeder vernünftige Sysadmin sollte sich für Python oder Perl entscheiden und nicht erwarten, dass die Shell alles
handhabt
16
Der Hauptzweck der Shell besteht darin, andere Programme auszuführen und Daten nicht direkt zu bearbeiten. Im Laufe der Jahre wurden einige externe Programme oder von ihnen bereitgestellte Funktionen (Globbing, Arithmetik printfusw.) in Shells integriert, wenn sie als nützlich genug erachtet wurden.
Chepner
8
Wenn Sie Ihr Skript auf codereview.stackexchange.com veröffentlichen, können die Rezensenten sicher einige Vorschläge machen, um Ihr Skript drastisch zu beschleunigen (oder zumindest aufzuzeigen, warum es in Python / etc anstelle von Shell geschrieben werden sollte).
Chepner
5
@ Kyle: awkein obligatorisches Dienstprogramm in POSIX und besonders gut geeignet (das, sehr schnell ist) Skripte zu implementieren , dass Sie sonst implementieren könnten mit sed, cut, tr, grep, und exprin einem Shell - Skript.
Nominal Animal

Antworten:

11

Von Shell-Skripten wird nicht erwartet, dass sie mit dieser Geschwindigkeit ausgeführt werden. Wenn Sie die Geschwindigkeit Ihres Skripts verbessern möchten, versuchen Sie es in Perl. Wenn das immer noch zu langsam ist, müssen Sie zu einer statisch typisierten Sprache wie Java oder C wechseln oder ein C-Modul für Perl schreiben, das die Teile ausführt, die zu langsam sind.

Shell ist die erste Ebene des Prototyping. Wenn Sie das Konzept mit Shell beweisen können, wechseln Sie zu einer besseren Skriptsprache, mit der mehr Grenzen überprüft werden können, als für die Shell erforderlich wären.

Es wird erwartet, dass ein Unix-Betriebssystem viele kleine Programme enthält, die genau definierte Aufgaben ausführen, die ein größeres Bild ergeben. Dies ist eine gute Sache, da es größere Programme unterteilt. Schauen Sie sich zum Beispiel qmail an und vergleichen Sie das mit sendmail. qmail besteht aus vielen Programmen:

http://www.nrg4u.com/qmail/the-big-qmail-picture-103-p1.gif

Wenn Sie den Netzwerkdämon ausnutzen, können Sie den Warteschlangenmanager nicht ausnutzen.

Ed Neville
quelle
Das OP bat ausdrücklich NICHT um Vorschläge zur Verbesserung der Geschwindigkeit des Codes. Die Frage war, warum bestimmte Dienstprogramme nicht wie cdoder eingebaut sind pwd.
Stephen C
4
Wahr. Die Antwort war, den Unterschied zwischen monolithisch und unterteilt auszudrücken und einen Grund für diese Bevorzugung zu zeigen.
Ed Neville
1
@StephenC cdist eingebaut - und muss es auch sein, da das Ändern des Arbeitsverzeichnisses in einem Unterprozess keine Auswirkungen auf übergeordnete Prozesse hat.
Jonas
67

Warum sind POSIX-Dienstprogramme nicht in die Shell integriert?

Da POSIX - kompatibel sein wird ein System benötigt 1 die meisten Dienstprogramme als Standalone - Befehle zur Verfügung zu stellen.

Wenn sie eingebaut würden, müssten sie an zwei verschiedenen Orten existieren, innerhalb und außerhalb der Hülle. Natürlich wäre es möglich, die externe Version zu implementieren, indem ein Shell-Skript-Wrapper für das eingebaute Skript verwendet wird, aber dies würde Nicht-Shell-Anwendungen benachteiligen, die die Dienstprogramme aufrufen.

Beachten Sie, dass BusyBox den von Ihnen vorgeschlagenen Weg eingeschlagen hat, indem Sie viele Befehle intern implementiert und die eigenständige Variante über Links zu sich selbst bereitgestellt haben. Ein Problem ist, dass der Befehlssatz zwar ziemlich umfangreich sein kann, die Implementierungen jedoch häufig eine Teilmenge des Standards darstellen und daher nicht kompatibel sind.

Beachten Sie, dass auch zumindest ksh93, bashund zshgeht weiter durch benutzerdefinierte Methoden für den laufend Shell dynamisch Last builtins von gemeinsam genutzten Bibliotheken bereitstellt. Technisch gesehen steht dann nichts mehr im Wege, alle POSIX-Dienstprogramme als Builtins zu implementieren und verfügbar zu machen.

Schließlich ist das Erzeugen neuer Prozesse mit modernen Betriebssystemen ein recht schneller Vorgang geworden. Wenn Sie wirklich von einem Leistungsproblem betroffen sind, gibt es möglicherweise einige Verbesserungen, um die Ausführung Ihrer Skripts zu beschleunigen.

1 POSIX.1-2008

Doch alle Standard - Dienstprogramme , einschließlich der regelmäßigen Einbauten in der Tabelle, aber nicht die speziellen Einbauten in Sonder Built-In Dienstprogramme beschrieben, wird in einer Art und Weise durchgeführt werden , so dass sie über die exec Familie zugegriffen werden kann funktioniert wie im Band System Interfaces von POSIX.1-2008 definiert und kann direkt von den Standarddienstprogrammen aufgerufen werden, die dies erfordern (env, find, nice, nohup, time, xargs).

jlliagre
quelle
4
Dies ist die richtige Antwort, aber ich möchte nur hinzufügen, dass die Schnittstelle dieser Dienstprogramme im Allgemeinen ohnehin über stdin / stdout erfolgt und dass sie, selbst wenn jede von ihnen als integrierte Routine in bash implementiert wäre, effektiv immer noch benötigt wird
Sich zu teilen
2
@Chunko Ja. Subshells sind jedoch leichter als Fork / Exec'ed-Prozesse.
Juli
3
@slebetman Du vermisst meinen Standpunkt. Subshells sind weder Threads noch ausgeführte Prozesse, unabhängig davon, ob sie unter Linux ausgeführt werden oder nicht. Subshells sind nur die Klone der Eltern, die von einem fork nicht folgenden Benutzer erstellt wurden exec. forkist heutzutage eine sehr leichte Operation im Vergleich zu exec.
Juli
3
Ich habe busybox noforkbuiltins mit etwa 10x weniger Overhead als noexecbuiltins gemessen, was wiederum ~ 5x weniger Overhead hatte als fork + exec einer separaten Binärdatei. Definitionen gemäß unix.stackexchange.com/a/274322/29483 Es ist interessant, dass die Busybox nicht noforkalles kann, obwohl ich weiß, dass ein Teil des Busybox-Codes dadurch verkürzt wird, dass der Speicher nicht aufgeräumt wird.
Sourcejedi
1
@jlliagre: Unter Linux erstellt ein Fork einen Prozess. Der Punkt, den Sie vielleicht vermissen, ist, dass sie unter Linux Prozesse so stark optimiert haben, dass die Entwickler festgestellt haben, dass es keinen weiteren Vorteil gibt, etwas Leichteres zu schaffen. Grundsätzlich ist ein Prozess unter Linux so leicht wie ein Thread.
Slebetman
9

Von dem BASH - Referenzhandbuch ,

Integrierte Befehle sind erforderlich, um Funktionen zu implementieren, die mit separaten Dienstprogrammen nicht oder nur schwer zu erhalten sind.

Wie Sie sicher gehört haben, stützt sich die UNIX-Philosophie in hohem Maße auf mehrere Anwendungen, die alle über eingeschränkte Funktionen verfügen. Jeder Einbau hat einen sehr guten Grund, warum er eingebaut ist. Alles andere ist es nicht. Ich denke, eine interessantere Klasse von Fragen ist "warum genau ist pwd eingebaut?".

Stephen C
quelle
2
Mit einem Wort: Modularität
Peschke
2
/ bin / pwd existiert. Ich denke, dies cdwäre ein besseres Beispiel für etwas, das sich nicht als separates Tool implementieren lässt.
Oskar Skog
1
@OskarSkog Das war der Punkt. cdmuss eingebaut werden, pwdnicht. Warum haben sich die bashImplementierer dafür entschieden, es aufzunehmen?
Stig Hemmer
1
... das von unix.stackexchange.com/questions/145479 abgedeckt wird .
JdeBP
@StigHemmer /bin/bashexistiert, ist aber noch eingebaut. Die Liste der eingebauten Funktionen finden Sie unter gnu.org/software/bash/manual/html_node/…
Stephen C
8

Die Jungs von AT & T haben sich dasselbe gefragt

Wenn Sie sich die Geschichte des AT & T-Software-Toolkits ansehen (das derzeit auf github ruht, seit das Kernteam abgereist ist), ist dies genau das, was es mit der AT & T-Korn-Shell, auch bekannt als ksh93, getan hat.

Leistung war immer ein Teil der Motivation für die ksh93-Betreuer, und beim Erstellen von ksh können Sie viele gängige POSIX-Dienstprogramme als dynamisch geladene Bibliotheken erstellen. Indem Sie diese Befehle an einen Verzeichnisnamen binden /opt/ast/bin, können Sie steuern, welche Version des Befehls verwendet wird, basierend auf der Position dieses Verzeichnisnamens in $PATH.

Beispiele:

cat chmod chown cksum cmp cp cut date expr fmt head join ln
mkdir mkfifo mktemp mv nl od paste rm tail tr uniq uuencode wc

Die vollständige Liste finden Sie im Archiv von github ast .

Beachten Sie, dass die meisten Ast-Tools ihre eigene Provenienz haben und sich stark von den allgemeineren Gnu-Implementierungen unterscheiden. Das AT & T-Forschungsteam hat die offiziellen Standards eingehalten. Auf diese Weise wurde Interoperabilität erreicht, wenn Code nicht freigegeben werden konnte.

Henk Langeveld
quelle
6

Wir haben also keine Ressourcen in die Optimierung des ursprünglichen Tools gesteckt, um jedem spezifischen Wunsch gerecht zu werden. Ich denke, wir müssen erklären, wie viel die Umsetzung dieses spezifischen Wunsches gekostet hätte.

POSIX definiert genug über die Dienstprogramme, so dass es kein großes Problem zu sein scheint, unterschiedliche Implementierungen zu haben.

das ist eine schlechte Annahme :-P.

Post-POSIX-Systeme werden aus guten Gründen immer leistungsfähiger und praktischer. als nachträglicher Standard holt es nie wirklich auf.

Ubuntu bemühte sich, zu einer reduzierten POSIX-Shell für Skripte zu wechseln, um den alten System V-Startvorgang zu optimieren. Ich sage nicht, dass es fehlgeschlagen ist, aber es hat viele Fehler ausgelöst, die bereinigt werden mussten: "bashisms", Skripte, die unter der /bin/shAnnahme ausgeführt wurden, dass bashFunktionen verfügbar waren.

POSIX sh ist keine gute Programmiersprache für allgemeine Zwecke. Sein primärer Zweck ist auch ein interaktiv Shell zu arbeiten. Denken Sie daran, dass Sie sich einem Turing-Tarpit nähern, sobald Sie beginnen, Ihre Befehle in einem Skript zu speichern . Es ist beispielsweise nicht möglich, Fehler in der Mitte einer normalen Pipeline zu erkennen . bashhinzugefügt set -o pipefail, aber dies ist nicht in POSIX.

Ähnliche nützliche, aber nicht standardisierte Funktionen werden von fast jedem Dienstprogramm bereitgestellt, das komplexer ist als true.

Für die Aufgabenklasse, die Sie skizzieren, können Sie eine grobe Linie zu Awk, Perl und heutzutage zu Python ziehen. Verschiedene Tools wurden erstellt und unabhängig voneinander weiterentwickelt. Würden Sie zB erwarten, dass GNU Awk in eine libutilposixextended subsumiert wird?

Ich sage nicht, dass wir jetzt einen allgemein besseren Ansatz haben, auf den ich Sie hinweisen kann. Ich habe eine Schwäche für Python. Awk ist überraschend mächtig, obwohl ich frustriert war, dass einige Funktionen spezifisch für GNU Awk sind. Der Punkt ist jedoch, dass die Verarbeitung einer großen Anzahl von Zeichenfolgen einzeln (vermutlich aus Zeilen der Dateien) kein Entwurfsziel der POSIX-Shell war.

sourcejedi
quelle
Ich frage mich, ob es irgendwelche Schwierigkeiten mit einer Shell geben würde, die davon ausgehen würde, dass ein Befehl, der aus einer konfigurierbaren Liste von Positionen ausgeführt wird, als eingebaut behandelt wird, wenn die Shell alles über den Befehl versteht. Wenn ein Skript ausgeführt wird, sollte cat -@fnord foodie Shell entscheiden, dass, da sie nicht weiß, was -@bedeutet, dass der eigentliche Befehl cat <foo >baraufgerufen werden muss, nur die Shell jedoch keinen weiteren Prozess auslösen muss.
Supercat
1
@ supercat Komplexität.
Sourcejedi
2

Es stellt sich auch die Frage: In welche Shell würden Sie sie einbauen?

Die meisten Unix / Linux-Systeme haben mehrere verschiedene Shells, die unabhängig voneinander entwickelt werden (sh / bash / korn / ???). Wenn Sie die Tools in die Shell einbauen, erhalten Sie für jede Shell eine andere Implementierung dieser Tools. Dies würde Overhead verursachen und Sie könnten abhängig von der Shell, die Sie zum Aufrufen verwendet haben, mit unterschiedlichen Funktionen / Fehlern in grep enden.

MTilsted
quelle
zsh ist heutzutage in manchen Kreisen sehr beliebt. csh / tcsh hatte historisch gesehen eine große Anhängerschaft, aber ich glaube, Sie sehen heute nicht viel davon. Und es gibt ein ganzes Bündel weniger bekannter Muscheln ...
einen Lebenslauf vom
Modularität. Mit Builtins müssen Sie die Shell jedes Mal neu kompilieren oder installieren, wenn eine Änderung an einem dieser Buildins vorgenommen wurde.
can-ned_food
1

Viele haben gut geantwortet. Ich möchte diese Antworten nur beglückwünschen. Ich denke, die UNIX-Philosophie ist, dass ein Tool eines tun und es gut machen sollte. Wenn man versucht, ein umfassendes Werkzeug zu entwickeln, ist das viel mehr Anlass zum Scheitern. Wenn Sie die Funktionalität auf diese Weise einschränken, erhalten Sie einen zuverlässigen Werkzeugsatz.

Überlegen Sie auch, ob Funktionen wie sed oder grep in die Shell integriert sind. Wäre es dann so einfach, sie über die Befehlszeile aufzurufen, wenn Sie möchten?

Betrachten Sie abschließend, dass einige der Funktionen, die Sie in BASH haben möchten, in BASH enthalten sind . Beispielsweise wird die Fähigkeit zum RE-Matching in BASH mithilfe des binären Operators = ~ implementiert (siehe Shell-Grammatik in der Manual Page , um genauer auf die Diskussion des Konstrukts [[]] für if zu verweisen ). Angenommen, ich suche in einer Datei nach 2 hexadezimalen Ziffern:

while read line; do
    if [[ $line =~ 0x[[:xdigit:]]{2} ]]; then
        # do something important with it
    fi
done < input_file.txt

Informationen zur sed-ähnlichen Funktionalität finden Sie unter Parametererweiterung in der Überschrift Erweiterung derselben Manpage. Sie werden eine Fülle von Dingen sehen, die an sed erinnern. Ich verwende sed am häufigsten, um eine Änderung des Ersetzungstyps in Text vorzunehmen. Aufbauend auf dem oben Gesagten:

# this does not take into account the saving of the substituted text
# it shows only how to do it
while read line; do
    ${line/pattern/substitution}
done < input_file.txt

Am Ende ist das obige "besser" als?

grep -E "[[:xdigit:]]{3}" input_file.txt
sed -e 's/pattern/substitution/' input_file.txt
Andrew Falanga
quelle
Ein Argument gegen die letzte Frage finden Sie unter unix.stackexchange.com/questions/169716/…
phk
1

Das ist wohl ein historischer Unfall.

Als UNIX Ende der 1960er und Anfang der 1970er Jahre erstellt wurde, verfügten Computer bei weitem nicht über so viel Arbeitsspeicher wie heute. Zu dieser Zeit wäre es möglich gewesen, all diese Funktionen als Shell-Built-in zu implementieren, aber aufgrund von Speicherbeschränkungen hätten sie den Umfang der Funktionen, die sie implementieren könnten, einschränken müssen, oder es bestünde das Risiko, nicht genügend Arbeitsspeicher zu haben und / oder den Papierkorb auszutauschen Probleme.

Auf der anderen Seite könnten sie durch Implementieren der gegebenen Funktionalität als separate Programme und durch Erleichtern der beiden erforderlichen Systemaufrufe zum Starten eines neuen Prozesses eine Skriptumgebung erstellen, die diese Probleme nicht aufweist und die immer noch zumutbar ist Geschwindigkeit.

Natürlich, wenn diese Dinge als separate Prozesse eingeführt werden, um sie von Programmen Menschen beginnen, die nicht Granaten, und dann wie , dass sie bleiben müssen, oder plötzlich das alles Software beginnt zu brechen.

Das soll nicht heißen, dass Sie einige Funktionen nicht zweimal implementieren können, und in der Tat implementieren einige Shells einige Funktionen, die ein externes Programm als eingebaute Shell sein sollen. zB implementiert bash den echoBefehl als eingebauten Befehl, aber es gibt auch einen/usr/bin/echo

Wouter Verhelst
quelle