Warum wird die Verwendung einer Shell-Schleife zum Verarbeiten von Text als schlechte Praxis angesehen?

196

Wird die Verwendung einer while-Schleife zum Verarbeiten von Text in POSIX-Shells allgemein als unangemessen angesehen?

Wie Stéphane Chazelas betonte, sind einige der Gründe, warum Shell Loop nicht verwendet wird, Konzept , Zuverlässigkeit , Lesbarkeit , Leistung und Sicherheit .

Diese Antwort erklärt die Zuverlässigkeits- und Lesbarkeitsaspekte :

while IFS= read -r line <&3; do
  printf '%s\n' "$line"
done 3< "$InputFile"

Für die Leistung sind die whileSchleife und das Lesen beim Lesen aus einer Datei oder einer Pipe enorm langsam, da die eingebaute Read-Shell jeweils ein Zeichen liest.

Wie wäre es mit konzeptionellen und Sicherheitsaspekten ?

cuonglm
quelle
Verwandte (die andere Seite der Medaille): Wie yesschreibt man so schnell in eine Datei?
Wildcard
1
Die eingebaute Lese-Shell liest nicht jeweils ein einzelnes Zeichen, sondern jeweils eine einzelne Zeile. wiki.bash-hackers.org/commands/builtin/read
A.Danischewski
@ A.Danischewski: Es kommt auf deine Muschel an. In bashliest es jeweils eine Puffergröße, versuchen Sie es dashzum Beispiel. Siehe auch unix.stackexchange.com/q/209123/38906
cuonglm

Antworten:

256

Ja, wir sehen eine Reihe von Dingen wie:

while read line; do
  echo $line | cut -c3
done

Oder schlimmer:

for line in `cat file`; do
  foo=`echo $line | awk '{print $2}'`
  echo whatever $foo
done

(nicht lachen, ich habe viele davon gesehen).

In der Regel von Shell-Scripting-Anfängern. Das sind naive wörtliche Übersetzungen dessen, was Sie in imperativen Sprachen wie C oder Python tun würden, aber so tun Sie Dinge nicht in Shells, und diese Beispiele sind sehr ineffizient, völlig unzuverlässig (was möglicherweise zu Sicherheitsproblemen führt) und falls Sie es jemals schaffen Um die meisten Fehler zu beheben, wird Ihr Code unleserlich.

Konzeptionell

In C oder den meisten anderen Sprachen liegen die Bausteine ​​nur eine Ebene über den Computeranweisungen. Sie teilen Ihrem Prozessor mit, was als Nächstes zu tun ist. Sie nehmen Ihren Prozessor bei der Hand und verwalten ihn im Mikromodus: Sie öffnen diese Datei, Sie lesen so viele Bytes, Sie tun dies, Sie tun das damit.

Muscheln sind eine höhere Sprache. Man kann sagen, es ist nicht einmal eine Sprache. Sie stehen vor allen Befehlszeileninterpreten. Die Aufgabe wird von den Befehlen erledigt, die Sie ausführen, und die Shell soll sie nur orchestrieren.

Eines der großartigen Dinge, die Unix eingeführt hat, waren die Pipe und die Standard-Streams stdin / stdout / stderr, die standardmäßig von allen Befehlen verarbeitet werden.

In 45 Jahren haben wir keine bessere API gefunden, um die Leistungsfähigkeit von Befehlen zu nutzen und sie bei einer Aufgabe zusammenarbeiten zu lassen. Das ist wahrscheinlich der Hauptgrund, warum die Leute heute noch Muscheln benutzen.

Sie haben ein Schneidwerkzeug und ein Transliterationswerkzeug und können einfach Folgendes tun:

cut -c4-5 < in | tr a b > out

Die Shell erledigt nur die Installation (Dateien öffnen, Pipes einrichten, Befehle aufrufen) und wenn alles fertig ist, fließt sie einfach, ohne dass die Shell etwas unternimmt. Die Werkzeuge erledigen ihre Arbeit gleichzeitig und effizient in ihrem eigenen Tempo, wobei genügend Puffer vorhanden sind, damit nicht einer den anderen blockiert. Es ist einfach wunderschön und doch so einfach.

Das Aufrufen eines Tools ist jedoch mit Kosten verbunden (und diese werden wir im Hinblick auf die Leistung entwickeln). Diese Tools können mit Tausenden von Anweisungen in C geschrieben sein. Es muss ein Prozess erstellt, das Tool geladen, initialisiert, dann bereinigt, der Prozess zerstört und gewartet werden.

Beim Aufrufen cutwird die Küchenschublade geöffnet. Nehmen Sie das Messer, verwenden Sie es, waschen Sie es, trocknen Sie es und legen Sie es wieder in die Schublade. Wenn Sie das tun:

while read line; do
  echo $line | cut -c3
done < file

Es ist, als würde man für jede Zeile der Datei das readWerkzeug aus der Küchenschublade holen (ein sehr ungeschicktes, weil es nicht dafür vorgesehen ist ), eine Zeile lesen, das Lesewerkzeug waschen und es wieder in die Schublade legen. Planen Sie dann eine Besprechung für das Werkzeug echound cut, holen Sie sie aus der Schublade, rufen Sie sie auf, waschen Sie sie, trocknen Sie sie, legen Sie sie wieder in die Schublade und so weiter.

Einige dieser Werkzeuge ( readund echo) sind in den meisten Schalen gebaut, aber das macht kaum einen Unterschied hier , da echound cutmüssen noch in separaten Prozessen ausgeführt werden.

Es ist, als würde man eine Zwiebel schneiden, aber man wäscht das Messer und legt es zwischen die Scheiben in die Küchenschublade zurück.

Hier ist es naheliegend, das cutWerkzeug aus der Schublade zu holen , die ganze Zwiebel in Scheiben zu schneiden und nach Beendigung der gesamten Arbeit wieder in die Schublade zu legen.

In Shells, insbesondere zum Verarbeiten von Text, rufen Sie so wenige Dienstprogramme wie möglich auf und lassen sie bei der Ausführung der Aufgabe zusammenarbeiten. Führen Sie nicht Tausende von Tools nacheinander aus, während Sie darauf warten, dass jedes gestartet, ausgeführt und bereinigt wird, bevor Sie das nächste ausführen.

Lesen Sie weiter in Bruce's feiner Antwort . Die internen Tools für die einfache Textverarbeitung in Shells (mit Ausnahme von zsh) sind begrenzt, umständlich und im Allgemeinen nicht für die allgemeine Textverarbeitung geeignet.

Performance

Wie bereits erwähnt, ist das Ausführen eines Befehls mit Kosten verbunden. Ein enormer Aufwand, wenn dieser Befehl nicht eingebaut ist, aber selbst wenn er eingebaut ist, sind die Kosten hoch.

Und Shells sind nicht so konzipiert, sie geben keinen Anspruch darauf, performante Programmiersprachen zu sein. Sie sind nicht, sie sind nur Befehlszeileninterpreter. In dieser Hinsicht wurde wenig optimiert.

Außerdem führen die Shells Befehle in separaten Prozessen aus. Diese Bausteine ​​haben keinen gemeinsamen Speicher oder Status. Wenn Sie ein fgets()oder fputs()in C ausführen, ist dies eine Funktion in stdio. stdio speichert interne Puffer für die Ein- und Ausgabe aller stdio-Funktionen, um zu vermeiden, dass teure Systemaufrufe zu oft ausgeführt werden.

Der entsprechende sogar eingebauten Schale Utilities ( read, echo, printf) , kann das nicht tun. readsoll eine Zeile lesen. Wenn es nach dem Zeilenumbruchzeichen steht, wird es beim nächsten Ausführen des Befehls übersehen. So readmuss die Eingabe ein Byte nach dem anderen gelesen werden (einige Implementierungen haben eine Optimierung, wenn die Eingabe eine reguläre Datei ist, indem sie Chunks lesen und zurücksuchen, aber das funktioniert nur für reguläre Dateien und bashliest zum Beispiel nur 128-Byte-Chunks, was bedeutet noch viel weniger als Textdienstprogramme).

Das Gleiche gilt für die Ausgabeseite. Sie echokann nicht nur ihre Ausgabe puffern, sondern muss sie sofort ausgeben, da der nächste Befehl, den Sie ausführen, diesen Puffer nicht freigibt.

Wenn Sie Befehle nacheinander ausführen, müssen Sie natürlich auf sie warten. Es ist ein kleiner Scheduler-Tanz, der die Steuerung von der Shell über die Tools bis hin zu den Werkzeugen übernimmt. Dies bedeutet auch, dass Sie (im Gegensatz zur Verwendung lang laufender Instanzen von Tools in einer Pipeline) nicht mehrere Prozessoren gleichzeitig nutzen können, wenn diese verfügbar sind.

Zwischen dieser while readSchleife und dem (angeblich) Äquivalent cut -c3 < filegibt es in meinem Schnelltest ein CPU-Zeitverhältnis von ungefähr 40000 in meinen Tests (eine Sekunde gegenüber einem halben Tag). Aber auch wenn Sie nur Shell-Builtins verwenden:

while read line; do
  echo ${line:2:1}
done

(hier mit bash), das ist immer noch ungefähr 1: 600 (eine Sekunde gegen 10 Minuten).

Zuverlässigkeit / Lesbarkeit

Es ist sehr schwer, diesen Code richtig zu machen. Die Beispiele, die ich gegeben habe, werden zu oft in freier Wildbahn gesehen, aber sie haben viele Fehler.

readist ein praktisches Werkzeug, das viele verschiedene Dinge kann. Es kann Eingaben vom Benutzer lesen, in Wörter aufteilen und in verschiedenen Variablen speichern. read lineist nicht eine Eingabezeile gelesen, oder vielleicht liest er eine Linie auf eine ganz besondere Art und Weise. Es liest tatsächlich Wörter aus der Eingabe, die durch einen $IFSBackslash getrennt sind und mit denen die Trennzeichen oder das Newline-Zeichen ausgeblendet werden können.

Mit dem Standardwert von $IFS, bei einer Eingabe wie:

   foo\/bar \
baz
biz

read linespeichert "foo/bar baz"in $line, nicht " foo\/bar \"wie man erwarten würde.

Um eine Zeile zu lesen, benötigen Sie tatsächlich:

IFS= read -r line

Das ist nicht sehr intuitiv, aber so ist es, denken Sie daran, dass Muscheln nicht so verwendet werden sollten.

Gleiches gilt für echo. echoerweitert Sequenzen. Sie können es nicht für beliebige Inhalte wie den Inhalt einer zufälligen Datei verwenden. Du brauchst printfstattdessen hier.

Und natürlich gibt es das typische Vergessen, eine Variable zu zitieren, in die jeder hineinfällt. Es ist also mehr:

while IFS= read -r line; do
  printf '%s\n' "$line" | cut -c3
done < file

Nun noch ein paar Vorsichtsmaßnahmen:

  • mit der Ausnahme zsh, dass dies nicht funktioniert, wenn die Eingabe NUL-Zeichen enthält, während zumindest GNU-Textdienstprogramme das Problem nicht hätten.
  • Wenn nach dem letzten Zeilenumbruch Daten vorhanden sind, werden diese übersprungen
  • Innerhalb der Schleife wird stdin umgeleitet, sodass Sie darauf achten müssen, dass die darin enthaltenen Befehle nicht aus stdin lesen.
  • Bei den Befehlen in den Schleifen achten wir nicht darauf, ob sie erfolgreich sind oder nicht. Normalerweise werden Fehlerzustände (Datenträger voll, Lesefehler ...) schlecht behandelt, normalerweise schlechter als mit dem richtigen Äquivalent.

Wenn wir einige der oben genannten Probleme angehen möchten, wird dies folgendermaßen aussehen:

while IFS= read -r line <&3; do
  {
    printf '%s\n' "$line" | cut -c3 || exit
  } 3<&-
done 3< file
if [ -n "$line" ]; then
    printf '%s' "$line" | cut -c3 || exit
fi

Das wird immer weniger lesbar.

Es gibt eine Reihe anderer Probleme bei der Übergabe von Daten an Befehle über die Argumente oder beim Abrufen ihrer Ausgabe in Variablen:

  • die Begrenzung der Größe von Argumenten (einige Implementierungen von Textdienstprogrammen haben auch dort eine Begrenzung, obwohl die Auswirkungen, die erreicht werden, im Allgemeinen weniger problematisch sind)
  • das NUL-Zeichen (auch ein Problem mit Textdienstprogrammen).
  • Argumente, die als Optionen verwendet werden, wenn sie mit -(oder +manchmal) beginnen
  • verschiedene Eigenheiten der verschiedenen Befehle , die typischerweise in diesen Schleifen verwendet , wie expr, test...
  • Die (eingeschränkten) Textmanipulationsoperatoren verschiedener Shells, die Mehrbyte-Zeichen auf inkonsistente Weise verarbeiten.
  • ...

Sicherheitsaspekte

Wenn Sie anfangen, mit Shell- Variablen und Argumenten für Befehle zu arbeiten , geben Sie ein Minenfeld ein.

Wenn Sie vergessen, Ihre Variablen in Anführungszeichen zu setzen , das Ende der Optionsmarkierung zu vergessen , in Gebietsschemata mit Mehrbyte-Zeichen zu arbeiten (heutzutage die Norm), werden Sie mit Sicherheit Fehler einführen, die früher oder später zu Schwachstellen werden.

Wenn Sie Loops verwenden möchten.

TBD

Stéphane Chazelas
quelle
24
Klar (lebhaft), lesbar und äußerst hilfreich. Vielen Dank noch mal. Dies ist die beste Erklärung, die ich jemals im Internet für den grundlegenden Unterschied zwischen Shell-Scripting und Programmierung gesehen habe.
Wildcard
2
Es sind Beiträge wie diese, die Anfängern helfen, etwas über Shell-Skripte zu lernen und die subtilen Unterschiede zu erkennen. Fügen Sie die referenzierende Variable als $ {VAR: -default_value} hinzu, um sicherzustellen, dass Sie keine Null erhalten. und setze -o nounset, um dich anzuschreien, wenn auf einen nicht definierten Wert verwiesen wird.
unsignedzero
6
@ A.Danischewski, ich denke du verpasst den Punkt. Ja, cutzum Beispiel ist effizient. cut -f1 < a-very-big-fileist effizient, so effizient, wie Sie es in C schreiben würden. Was fürchterlich ineffizient und fehleranfällig ist, ruft cutfür jede Zeile einer a-very-big-filein einer Shell-Schleife auf, auf die in dieser Antwort verwiesen wird. Das stimmt mit Ihrer letzten Aussage über das Schreiben von unnötigem Code überein, die mich denken lässt, dass ich Ihren Kommentar vielleicht nicht verstehe.
Stéphane Chazelas
5
"In 45 Jahren haben wir keine bessere API gefunden, um die Leistungsfähigkeit von Befehlen zu nutzen und sie bei einer Aufgabe zusammenarbeiten zu lassen." - Tatsächlich hat PowerShell das gefürchtete Parsing-Problem gelöst, indem strukturierte Daten und keine Byteströme weitergegeben wurden. Der einzige Grund, warum Shells es noch nicht verwenden (die Idee ist schon eine ganze Weile da und hat sich irgendwann um Java herum herauskristallisiert, als die jetzt üblichen List & Dictionary-Containertypen zum Mainstream wurden), ist, dass sich die Betreuer noch nicht einig waren gemeinsames strukturiertes Datenformat (.
ivan_pozdeev
6
@OlivierDulac Ich denke, das ist ein bisschen Humor. Dieser Abschnitt wird für immer TBD sein.
muru
43

Was das Konzept und die Lesbarkeit betrifft, sind Shells normalerweise an Dateien interessiert. Ihre "adressierbare Einheit" ist die Datei und die "Adresse" ist der Dateiname. Shells bieten alle Arten von Methoden zum Testen der Existenz von Dateien, des Dateityps und der Dateinamenformatierung (beginnend mit Globbing). Shells haben nur sehr wenige Grundelemente für den Umgang mit Dateiinhalten. Shell-Programmierer müssen ein anderes Programm aufrufen, um mit Dateiinhalten umzugehen.

Aufgrund der Ausrichtung der Datei und des Dateinamens ist die Textbearbeitung in der Shell, wie Sie bereits bemerkt haben, sehr langsam, erfordert jedoch auch einen unklaren und verzerrten Programmierstil.

Bruce Ediger
quelle
25

Es gibt einige komplizierte Antworten, die viele interessante Details für die Geeks unter uns liefern, aber es ist wirklich ganz einfach - die Verarbeitung einer großen Datei in einer Shell-Schleife ist einfach zu langsam.

Ich denke, dass der Fragesteller an einer typischen Art von Shell-Skript interessiert ist, die mit einem Kommandozeilen-Parsing, Umgebungseinstellungen, Überprüfen von Dateien und Verzeichnissen und etwas mehr Initialisierung beginnen kann, bevor er zu seinem Hauptjob übergeht: Durchlaufen eines großen Skripts zeilenorientierte Textdatei.

Für die ersten Teile ( initialization) spielt es normalerweise keine Rolle, dass Shell-Befehle langsam sind - es werden nur ein paar Dutzend Befehle ausgeführt, möglicherweise mit ein paar kurzen Schleifen. Selbst wenn wir diesen Teil ineffizient schreiben, dauert es normalerweise weniger als eine Sekunde, um die gesamte Initialisierung durchzuführen, und das ist in Ordnung - es passiert nur einmal.

Wenn wir uns jedoch mit der Verarbeitung der großen Datei befassen, die Tausende oder Millionen von Zeilen enthalten kann, ist es nicht in Ordnung, dass das Shell-Skript einen signifikanten Bruchteil einer Sekunde (selbst wenn es nur ein paar Dutzend Millisekunden dauert) für jede Zeile benötigt. Das kann sich auf Stunden summieren.

Dann müssen wir andere Tools verwenden, und das Schöne an Unix-Shell-Skripten ist, dass sie es uns sehr einfach machen, das zu tun.

Anstatt eine Schleife zu verwenden, um jede Zeile zu betrachten, müssen wir die gesamte Datei durch eine Pipeline von Befehlen leiten . Dies bedeutet, dass die Shell die Befehle nicht Tausende oder Millionen Mal aufruft, sondern nur einmal. Es ist wahr, dass diese Befehle Schleifen haben, um die Datei zeilenweise zu verarbeiten, aber es handelt sich nicht um Shell-Skripte, und sie sind schnell und effizient gestaltet.

Unix hat viele wundervolle eingebaute Werkzeuge, von einfachen bis hin zu komplexen, mit denen wir unsere Pipelines bauen können. Normalerweise beginne ich mit den einfachen und verwende komplexere, wenn nötig.

Ich würde auch versuchen, mich an Standard-Tools zu halten, die auf den meisten Systemen verfügbar sind, und versuchen, meine Nutzung portabel zu halten, obwohl dies nicht immer möglich ist. Und wenn Ihre Lieblingssprache Python oder Ruby ist, macht es Ihnen vielleicht nichts aus, wenn Sie sicherstellen, dass es auf jeder Plattform installiert ist, auf der Ihre Software ausgeführt werden soll :-)

Einfache Werkzeuge umfassen head, tail, grep, sort, cut, tr, sed, join(beim Einarbeiten 2 - Dateien) und awkEinzeiler, unter vielen anderen. Es ist erstaunlich, was manche Leute mit Pattern Matching und sedBefehlen anfangen können.

Wenn es komplexer wird und Sie wirklich eine Logik auf jede Zeile anwenden müssen, awkist dies eine gute Option - entweder ein Einzeiler (einige Leute setzen ganze awk-Skripte in 'eine Zeile', obwohl das nicht sehr gut lesbar ist) oder in a kurzes externes Skript.

Da awkes sich um eine interpretierte Sprache (wie Ihre Shell) handelt, ist es erstaunlich, dass sie zeilenweise so effizient verarbeitet werden kann, aber sie wurde speziell dafür entwickelt und ist wirklich sehr schnell.

Und dann gibt es noch Perleine Vielzahl anderer Skriptsprachen, die sehr gut Textdateien verarbeiten können und auch viele nützliche Bibliotheken enthalten.

Und schließlich gibt es ein gutes altes C, wenn Sie maximale Geschwindigkeit und hohe Flexibilität benötigen (obwohl die Textverarbeitung etwas mühsam ist). Aber es ist wahrscheinlich eine sehr schlechte Zeit, ein neues C-Programm für jede andere Dateiverarbeitungsaufgabe zu schreiben, auf die Sie stoßen. Ich arbeite viel mit CSV-Dateien, daher habe ich mehrere allgemeine Dienstprogramme in C geschrieben, die ich in vielen verschiedenen Projekten wiederverwenden kann. Tatsächlich erweitert dies die Palette der 'einfachen, schnellen Unix-Tools', die ich von meinen Shell-Skripten aus aufrufen kann, so dass ich die meisten Projekte nur durch das Schreiben von Skripten bewältigen kann.

Einige abschließende Hinweise:

  • Vergessen Sie nicht, Ihr Hauptshell-Skript mit zu starten export LANG=C, oder viele Tools behandeln Ihre normalen ASCII-Dateien als Unicode, wodurch sie viel langsamer werden
  • Überlegen export LC_ALL=CSie auch, ob Sie sortunabhängig von der Umgebung eine konsistente Reihenfolge erzielen möchten.
  • Wenn Sie sortIhre Daten benötigen , wird dies wahrscheinlich mehr Zeit (und Ressourcen: CPU, Speicher, Festplatte) in Anspruch nehmen als alles andere. Versuchen Sie daher, die Anzahl der sortBefehle und die Größe der zu sortierenden Dateien zu minimieren
  • Wenn möglich, ist eine einzelne Pipeline in der Regel am effizientesten. Wenn Sie mehrere Pipelines nacheinander mit Zwischendateien ausführen, kann dies zwar besser gelesen und behoben werden, die Zeit, die Ihr Programm benötigt, wird jedoch länger
Laurence Renshaw
quelle
6
Pipelines von vielen einfachen Werkzeugen (insbesondere die erwähnten, wie Kopf, Schwanz, Grep, Sortieren, Schneiden, Tr, Sed, ...) werden oft unnötig verwendet, insbesondere wenn Sie bereits eine awk-Instanz in dieser Pipeline haben, die dies kann auch die Aufgaben dieser einfachen Werkzeuge. Ein weiteres zu berücksichtigendes Problem besteht darin, dass Sie in Pipelines Statusinformationen nicht einfach und zuverlässig von Prozessen auf der Vorderseite einer Pipeline an Prozesse auf der Rückseite übergeben können. Wenn Sie für solche Pipelines von einfachen Programmen ein awk-Programm verwenden, haben Sie einen einzigen Zustandsraum.
Janis
14

Ja aber...

Die richtige Antwort von Stéphane Chazelas basiert auf - Konzept der jeden Text Operation auf bestimmte Binärdateien zu delegieren, wie grep, awk, sedund andere.

Da in der Lage ist, eine Menge Dinge selbst zu erledigen, kann es sein , dass das Fallenlassen von Gabeln schneller erfolgt (sogar als das Ausführen eines anderen Interpreters für alle Aufgaben).

Schauen Sie sich zum Beispiel diesen Beitrag an:

https://stackoverflow.com/a/38790442/1765658

und

https://stackoverflow.com/a/7180078/1765658

testen und vergleichen ...

Na sicher

Es wird nicht auf Benutzereingaben und Sicherheit geachtet !

Schreiben Sie keine Webanwendung unter !!

Bei vielen Serververwaltungsaufgaben, bei denen anstelle von , kann die Verwendung von builtins bash sehr effizient sein.

Meine Bedeutung:

Das Schreiben von Tools wie bin utils ist nicht die gleiche Arbeit wie die Systemadministration.

Also nicht die gleichen Leute!

Wo Sysadmins Bescheid wissen müssen shell, können sie mit seinem bevorzugten (und bekanntesten) Tool Prototypen schreiben .

Wenn dieses neue Hilfsprogramm (Prototyp) wirklich nützlich ist, könnten einige andere Leute ein spezielles Werkzeug entwickeln, indem sie eine geeignetere Sprache verwenden.

F. Hauri
quelle
1
Gutes Beispiel. Ihr Ansatz ist mit Sicherheit effizienter als der von Lololux. Beachten Sie jedoch, dass die Antwort von Tensibai (der richtige Weg, diese IMO durchzuführen , ohne Shell-Loops zu verwenden) um Größenordnungen schneller ist als Ihre. Und deins ist viel schneller, wenn Sie es nicht benutzen bash. (über 3 mal so schnell mit ksh93 in meinem Test auf meinem System). bashist in der Regel die langsamste Schale. Even zshist in diesem Skript doppelt so schnell. Sie haben auch ein paar Probleme mit nicht zitierten Variablen und der Verwendung von read. Sie veranschaulichen hier also tatsächlich viele meiner Punkte.
Stéphane Chazelas
@StéphaneChazelas Ich stimme zu, Bash ist wahrscheinlich die langsamste Shell, die Menschen heute verwenden könnten, aber die am häufigsten verwendete.
F. Hauri
@ StéphaneChazelas Ich habe eine Perl- Version auf meiner Antwort
F. Hauri
1
@Tensibai, werden Sie feststellen , POSIXsh , Awk , Sed , grep, ed, ex, cut, sort, join... alle mit mehr Zuverlässigkeit als Bash oder Perl.
Wildcard
1
@Tensibai, von allen von U & L betroffenen Systemen sind die meisten (Solaris, FreeBSD, HP / UX, AIX, die meisten eingebetteten Linux-Systeme ...) bashstandardmäßig nicht installiert. bashwird meist nur auf Apple MacOS und GNU - Systemen (ich nehme an das ist , was Sie nennen fand große Distributionen ), obwohl viele Systeme auch haben es als optionales Paket (wie zsh, tcl, python...)
Stéphane Chazelas