Können tr-Befehle verkettet werden, um mehrere tr-Prozesse in einer Pipeline zu vermeiden?

11

Ich habe eine Reihe von txt-Dateien. Ich möchte sie in Kleinbuchstaben ausgeben, nur in alphabetischer Reihenfolge und mit einem Wort pro Zeile. Ich kann dies mit mehreren trBefehlen in einer Pipeline wie der folgenden tun :

tr -d '[:punct:]' <doyle_sherlock_holmes.txt | tr '[:upper:]' '[:lower:]' | tr ' ' '\n'

Ist dies in einem Scan möglich? Ich konnte ein C - Programm zu schreiben , dies zu tun, aber ich fühle mich wie es eine Möglichkeit, es zu tun mit tr, sed, awkoder perl.

Tlehman
quelle
Welches Betriebssystem verwenden Sie? Haben Sie Zugriff auf die GNU-Tools?
Terdon

Antworten:

9

Sie können mehrere Übersetzungen kombinieren (mit Ausnahme komplexer Fälle mit überlappenden, vom Gebietsschema abhängigen Mengen), aber Sie können das Löschen nicht mit der Übersetzung kombinieren.

<doyle_sherlock_holmes.txt tr -d '[:punct:]' | tr '[:upper:] ' '[:lower:]\n'

Zwei Aufrufe von trsind wahrscheinlich schneller als ein einzelner Aufruf komplexerer Tools. Dies hängt jedoch stark von der Eingabegröße, den Anteilen verschiedener Zeichen, der Implementierung trund den konkurrierenden Tools, dem Betriebssystem und der Anzahl ab von Kernen usw.

Gilles 'SO - hör auf böse zu sein'
quelle
Ich bin nicht sicher, ob ich wieder kombinieren solltr -s '[:upper:] [:punct:]' '[:lower:]\n' <doyle_sherlock_holmes.txt
Costas
1
@Costas Das würde Interpunktion in Zeilenumbrüche umwandeln. Es mag für diese spezielle Anwendung in Ordnung sein, aber die Ausgabe stimmt nicht mit der des Originals überein.
Gilles 'SO - hör auf böse zu sein'
@Costas - während die Newline-Sache hier akzeptabel sein könnte, denke ich nicht, dass es wäre, die Großbuchstaben zu drücken. Zum Beispiel: printf 'A.AAAA,A' | tr -s '[:upper:] [:punct:]' '[:lower:][\n*]'get a\na\na', und die Transformation für hat ... '[:lower:]\n'möglicherweise nicht unbedingt irgendetwas zu tun '[:punct:]'- einige trs kürzen set1 auf Übereinstimmung mit 2 und andere führen eine implizite Aktion aus [\n*]. Es ist besser, nur die Reichweite dort zu nutzen.
Mikeserv
4

Hier einige Ansätze:

  • GNU grepund tr: Finde alle Wörter und mache sie in Kleinbuchstaben

    grep -Po '\w+' file | tr '[A-Z]' '[a-z]'
  • GNU grep und perl: wie oben, aber perl übernimmt die Konvertierung in Kleinbuchstaben

    grep -Po '\w+' file | perl -lne 'print lc()'
  • Perl: Finde alle alphabetischen Zeichen und drucke sie in Kleinbuchstaben aus (danke @steeldriver):

    perl -lne 'print lc for /[a-z]+/ig' file
  • sed: Entfernen Sie alle Zeichen, die keine Buchstaben oder Leerzeichen sind, ersetzen Sie alle alphabetischen Zeichen durch ihre Kleinbuchstabenversionen und ersetzen Sie alle Leerzeichen durch Zeilenumbrüche. Beachten Sie, dass dies voraussetzt, dass alle Leerzeichen Leerzeichen und keine Tabulatoren sind.

    sed 's/[^a-zA-Z ]\+//g;s/[a-zA-Z]\+/\L&/g; s/ \+/\n/g' file
terdon
quelle
2
Würde so etwas auch gerne perl -lne 'print lc for /[[:alpha:]]+/g'funktionieren? oder ist es schlechter Stil? (Ich bin neu in Perl und versuche zu lernen!)
Steeldriver
@steeldriver ja das wäre es, schön! Wenn Sie Perl lernen, sind Sie sicher auf das Motto gestoßen : TMTOWTDI :) Danke, ich werde dieses hinzufügen.
Terdon
3
Mit neuer Version (> 4.2.1)sed -z 's/\W*\(\w\+\)\W*/\L\1\n/g'
Costas
@Costas ah, sedkann \wjetzt? Cool!
Terdon
@terdon - es gemacht , dass für eine Weile, aber, da Costas nicht erwähnt hat, ich glaube , das Interessanteste , was über den oben genannten Kommentar ist GNU sed‚s -zero abgrenzen Schalter - es Zyklen über \0NULs statt Zeilenumbrüchen. Ziemlich cool, wenn du so etwas machst tar -c . | tr -s \\0 | sed -z ...- aber irgendwie langsam.
Mikesserv
3

Ja. Sie können dies mit treinem ASCII-Gebietsschema tun (was für eine GNU trohnehin die einzige Aufgabe ist) . Sie können die POSIX-Klassen verwenden oder die Bytewerte jedes Zeichens durch eine Oktalzahl referenzieren. Sie können ihre Transformationen auch auf Bereiche aufteilen.

LC_ALL=C tr '[:upper:]\0-\101\133-140\173-\377' '[:lower:][\n*]' <input

Der obige Befehl würde alle Großbuchstaben in Kleinbuchstaben umwandeln, Kleinbuchstaben vollständig ignorieren und alle anderen Zeichen in Zeilenumbrüche umwandeln. Natürlich haben Sie dann eine Menge Leerzeilen. Der tr -sSchalter "Queeeze Repeats" kann in diesem Fall nützlich sein. Wenn Sie ihn jedoch neben der [:upper:]to- [:lower:]Transformation verwenden, werden auch Großbuchstaben gedrückt . Auf diese Weise benötigt es noch einen zweiten Filter wie ...

LC... tr ... | tr -s \\n

...oder...

LC... tr ... | grep .

... und so wird es viel weniger bequem als ...

LC_ALL=C tr -sc '[:alpha:]' \\n <input | tr '[:upper:]' '[:lower:]'

... das die -cErgänzung von alphabetischen Zeichen nacheinander zu einer einzigen neuen Zeile pro Stück zusammenpresst und dann die Transformation von oben nach unten auf der anderen Seite der Pipe durchführt.

Das heißt nicht, dass Bereiche dieser Art nicht nützlich sind. Zeug wie:

tr '\0-\377' '[1*25][2*25][3*25][4*25][5*25][6*25][7*25][8*25][9*25][0*]' </dev/random

... kann sehr praktisch sein, da es die Eingabebytes über ein Spreizspektrum ihrer Werte in alle Ziffern konvertiert. Verschwenden Sie nicht, wollen Sie nicht, wissen Sie.

Ein anderer Weg, um die Transformation durchzuführen, könnte beinhalten dd.

tr '\0-\377' '[A*64][B*64][C*64][D*64]' </dev/urandom |
dd bs=32 cbs=8 conv=unblock,lcase count=1

dadbbdbd
ddaaddab
ddbadbaa
bdbdcadd

Da ddsowohl Konvertierungen unblockals auch lcaseKonvertierungen gleichzeitig ausgeführt werden können, ist es möglicherweise sogar möglich, einen Großteil der Arbeit an diese weiterzugeben. Dies kann jedoch nur dann wirklich nützlich sein, wenn Sie die Anzahl der Bytes pro Wort genau vorhersagen können - oder zumindest jedes Wort zuvor mit Leerzeichen auf eine vorhersagbare Byteanzahl auffüllen können, da unblockam Ende jedes Blocks nachgestellte Leerzeichen verwendet werden.

mikeserv
quelle
+2 Bonuspunkte für das dd
Mitmachen
@TobiLehman - Ich freue mich sehr, dass Sie zustimmen.
Mikeserv