Golftipps in sed

19

Welche allgemeinen Tipps haben Sie zum Golfen in Sed? Ich bin auf der Suche nach Ideen, die sich auf Code-Golf-Probleme anwenden lassen und die zumindest etwas spezifisch für sed sind (z. B. "Kommentare entfernen" ist keine Antwort).

Bitte posten Sie einen Tipp pro Antwort.

Toby Speight
quelle
4
Kein wirklicher Golftipp (aber immer noch ein Tipp zum Golfen): Zeilenvorschübe verbrauchen genauso viele Bytes wie Semikolons, sodass Sie Ihren Code kurz und lesbar halten können.
Dennis
Auch kein Tipp, aber ein Problem: Ich habe GNU sed, aber der FBefehl hat nie funktioniert. Weiß jemand warum?
Seshoumara
@seshoumara Farbeitet an meiner GNU sed (Debian-Test). Es wird -natürlich nur gedruckt, wenn von stdin gelesen wird, aber das wird erwartet. Von was bekommst sed -e 'F;Q' /etc/hostnamedu
Toby Speight
@TobySpeight Das gibt diesen Fehler: char 1: unknown command: F. Ich muss sed vielleicht updaten; welche version hast du Der LBefehl funktioniert auch nicht, aber er ist sowieso nutzlos, da er -l nexistiert. Alles andere, was auf der Site von GNU sed erwähnt wird, funktioniert.
Seshoumara
1
Ich habe den Chatroom bash, sed and dcfür alle geöffnet, die über diese Sprachen sprechen und fragen möchten. Lasst uns eine Community bilden!
Seshoumara

Antworten:

11

Wenn Sie Beschriftungen verwenden müssen, möchten Sie sicher, dass Ihre Beschriftungsnamen so kurz wie möglich sind. Im Extremfall können Sie die leere Zeichenfolge sogar als Labelnamen verwenden:

:    # define label ""
p    # print pattern space
b    # infinite loop! - branch to label ""
Digitales Trauma
quelle
4
Ab Version 4.3 wurde dieses Verhalten beseitigt . :Benötigt jetzt ein Etikett.
Kevin
In der Tat ist hier auch der eigentliche Git-Commit- Link . Ich denke für PPCG ändert sich nicht viel, da wir Antworten für GNU sed 4.2.x posten dürfen, aber es ist gut zu wissen, dass dieser Trick leider nicht mehr offiziell funktioniert.
Seshoumara
8

Die GNU sed Dokumentation beschreibt den sBefehl als "sed's Swiss Army Knife" . Wenn Sie jedoch nur alle Instanzen eines Zeichens durch ein anderes ersetzen möchten, benötigen Sie den folgenden yBefehl:

y/a/b/

ist ein Zeichen kürzer als:

s/a/b/g
Digitales Trauma
quelle
Es ist auch viel schneller und kann Zeichen an Ort und Stelle tauschen:y/12/21/
mikeserv
6

Erwägen Sie die Verwendung einer erweiterten Regex-Syntax (in GNU sed). Die -rOption kostet ein Byte in der Wertung, aber die einmalige Verwendung, um die Backslashes eines Paares zu eliminieren, \(...\)hat sich bereits bezahlt gemacht.

Toby Speight
quelle
2
Mit dem zusätzlichen Hinweis, -rder GNU- sedspezifisch zu sein scheint .
Manatwork
@manat - hinzugefügt (aber es ist eine Community-Wiki-Antwort, also hättest du dich selbst bearbeiten können).
Toby Speight
Na sicher. Ich habe es nur nicht als Teil des Trinkgeldes angesehen, sondern nur als zusätzlichen Hinweis.
Manatwork
Und es hält sich die Zahlung bei der Verwendung von +, ?, {}und |in Regex -Übereinstimmungen, da keine Schrägstriche entweder benötigt werden.
Seshoumara
-Efunktioniert als Alias ​​für -rviele sedImplementierungen, wenn ich mich richtig erinnere.
Phk
6

Beim wiederholten Ersetzen in einer Schleife:

loop:
s/foo/bar/g
tloop

Es ist normalerweise nicht erforderlich, global zu ersetzen, da die Schleife schließlich alle Vorkommen ersetzt:

# GNU sed
:
s/foo/bar/
t

Beachten Sie auch die GNU-Erweiterung oben: Ein Label kann einen leeren Namen haben, wodurch mehr wertvolle Bytes gespart werden. In anderen Implementierungen kann ein Label nicht leer sein, und das Springen ohne Label überträgt den Fluss an das Ende des Skripts (dh dasselbe wie n).

Toby Speight
quelle
1
Der leere Labelname ist GNU-spezifisch, für POSIX sind Verzweigungen ohne Argument erforderlich, um zum Ende des Skripts zu springen (dies scheint das Verhalten in BSDs und Busybox zu sein, auch in GNU sed, wenn Sie kein :
Leerzeichen
2
Das namenlose Label war immer ein Fehler in GNU sed, keine Erweiterung, und in Version 4.3 und höher wurde dieser Fehler leider behoben. Sehen Sie hier .
Seshoumara
5

Es gibt keine eingebaute Arithmetik, aber Berechnungen können in unären oder unärkodierten Dezimalzahlen durchgeführt werden. Der folgende Code konvertiert Dezimalzahlen in UCD mit x als Einheit und 0 als Zifferntrennzeichen:

s/[1-9]/0&/g
s/[5-9]/4&/g
y/8/4/
s/9/4&/g
s/4/22/g
s/[37]/2x/g
s/[26]/xx/g
s/[1-9]/x/g

und hier ist die Umrechnung zurück in dezimal:

s/0x/-x/g
s/xx/2/g
y/x/1/
s/22/4/g
s/44/8/g
s/81/9/g
s/42/6/g
s/21/3/g
s/61/7/g
s/41/5/g
s/-//g

Diese beiden Werte stammen aus einer Antwort auf "Multiplizieren Sie zwei Zahlen ohne Verwendung von Zahlen" .

Mit diesem Schleifenpaar aus dieser Antwort zu "{Curly Numbers};" kann eine einfache alte Unäre konvertiert werden. , wo das Gerät ist ;. Ich habe vund verwendet x, um Roman für 5und zu entsprechen 10; bkommt von "bis".

# unary to decimal
:d
/;/{
s/;;;;;/v/g
s/vv/x/g
/[;v]/!s/x\+/&0/
s/;;/b/g
s/bb/4/
s/b;/3/
s/v;/6/
s/vb/7/
s/v3/8/
s/v4/9/
y/;bvx/125;/
td
}

# Decimal to unary
:u
s/\b9/;8/
s/\b8/;7/
s/\b7/;6/
s/\b6/;5/
s/\b5/;4/
s/\b4/;3/
s/\b3/;2/
s/\b2/;1/
s/\b1/;0/
s/\b0//
/[^;]/s/;/&&&&&&&&&&/g
tu
Toby Speight
quelle
1
... und wenn Sie eines von beiden verwenden müssen, haben Sie mit ziemlicher Sicherheit bereits den Code Golf verloren, obwohl Sie möglicherweise immer noch mit Java-Antworten konkurrieren können ;-) Trotzdem macht die Verwendung Spaß.
Digital Trauma
Die Konvertierung von unär nach dezimal liefert falsche Antworten für unäres Eingabeäquivalent der Dezimalform X0X, z. B. 108. Die dafür verantwortliche Zeile ist /[;v]/!s/\b/0/2, die geändert werden muss, /[;v]/!s:x\+:&0:damit sie funktioniert. Sehen Sie hier .
Seshoumara
@seshoumara, Ihr Link scheint eine leere Seite zu sein. Es ist jedoch durchaus plausibel, dass ich beim Extrahieren dieses Codes aus der angegebenen Antwort einen Fehler gemacht habe. Ich werde also einfach Ihre Korrektur anwenden.
Toby Speight
Der Link wird korrekt geladen, aber ich habe etwas anderes erwartet als eine graue Seite mit "TIO" und etwas, das wie das Ubuntu-Logo aussieht - ist das beabsichtigt? Und ich bezog mich auf die zweite der Antworten, auf die ich mich bezog ( 58007 ), da hier die einfach-unäre Stichprobe entstand.
Toby Speight
Die TIO-Verbindung sollte den korrigierten Code sowie eine Beispieleingabe 108 in unary enthalten. Wenn Sie den Code ausführen, sollten Sie das richtige Ergebnis 108 und nicht 180 sehen, wie es zuvor von dieser jetzt festgelegten Codezeile generiert wurde. Das Aktualisieren der Antwort, auf die verwiesen wird, liegt ganz bei Ihnen. Dies ist ein Community-Wiki.
Seshoumara
4

Wie in man sed(GNU) erwähnt, können Sie mithilfe der Syntax ein beliebiges Zeichen als Begrenzer für reguläre Ausdrücke verwenden

\%regexp%

wo %ist ein Platzhalter für ein beliebiges Zeichen.

Dies ist nützlich für Befehle wie

/^http:\/\//

die sind kürzer als

\%^http://%

Was ist in der erwähnt wird GNU sed Handbuch aber nicht in man sedist , dass Sie die Trennzeichen von ändern können s///und y///wie gut.

Zum Beispiel der Befehl

ss/ssg

Entfernt alle Schrägstriche aus dem Musterbereich.

Dennis
quelle
4

Wenn die Frage dies nicht ausdrücklich verbietet, besteht der Konsens für diese Meta-Frage darin, dass die numerische Eingabe unär sein kann. Dies erspart Ihnen die 86 Bytes Dezimalzahl nach dieser Antwort als unär .

Digitales Trauma
quelle
Ist das nicht ein Metakonsens für sed, der sich auf ein einfaches altes unäres Format bezieht? Ich habe mehrere Antworten, bei denen mir eine Eingabe in UCD helfen würde, falls es so oder so ist.
Seshoumara
@seshoumara Ich meinte unary, nicht UCD
Digital Trauma
Durch die Konvertierung von dezimal nach normal werden 126 Byte gemäß der von Ihnen verknüpften Antwort gespart. Die 86 Bytes sind für die Konvertierung in UCD.
Seshoumara
4

Um diesen Tipp bezüglich der Konvertierungen zwischen dezimalem und einfachem unärem Zahlenformat zu erweitern, präsentiere ich die folgenden alternativen Methoden mit ihren Vor- und Nachteilen.

Dezimal bis unär: 102 + 1 (r Flag) = 103 Bytes. Ich habe \tals 1-Byte-Tabulator gezählt .

h
:
s:\w::2g
y:9876543210:87654321\t :
/ /!s:$:@:
/\s/!t
x;s:-?.::;x
G;s:\s::g
/\w/{s:@:&&&&&&&&&&:g;t}

Probieren Sie es online!

Vorteil: Es ist 22 Bytes kürzer und arbeitet zusätzlich mit negativen ganzen Zahlen als Eingabe

Nachteil: es überschreibt den Laderaum. Da es jedoch wahrscheinlicher ist, dass Sie die Eingabe-Ganzzahl gleich zu Beginn des Programms konvertieren müssen, ist diese Einschränkung selten zu spüren.

Einzahlig bis dezimal: 102 + 1 (r Flag) = 103 Bytes

s:-?:&0:
/@/{:
s:\b9+:0&:
s:.9*@:/&:
h;s:.*/::
y:0123456789:1234567890:
x;s:/.*::
G;s:\n::
s:@::
/@/t}

Probieren Sie es online!

Vorteil: es ist 14 Bytes kürzer. Dieses Mal funktionieren beide Tippversionen für negative Ganzzahlen als Eingabe.

Nachteil: es überschreibt den Laderaum

Bei einer komplizierten Herausforderung müssen Sie diese Ausschnitte anpassen, um mit anderen Informationen zu arbeiten, die neben der zu konvertierenden Zahl möglicherweise im Muster- oder Haltebereich vorhanden sind. Der Code kann mehr gespielt werden, wenn Sie wissen, dass Sie nur mit positiven Zahlen arbeiten oder dass eine Null allein keine gültige Eingabe / Ausgabe ist.

Ein Beispiel für eine solche Herausforderungsantwort, bei der ich diese Ausschnitte erstellt und verwendet habe, ist der Kehrwert einer Zahl (1 / x) .

Seshoumara
quelle
Für einstellige-Dezimal können Sie zwei Bytes speichern , indem Sie die letzten beiden Substitutionen kombiniert: s:\n|@$::g. tio.run/##K05N@f@/2ErX3krNwIpL30G/…
Jordan
Ich hatte meinen eigenen Versuch am Dezimal-Unary-Konverter. Hier sind 97 Bytes :) Probieren Sie es online! (Erfordert auch nicht -r, aber mit neuem Konsens werden Flaggen sowieso nicht für das Bytecount gezählt, und der Laderaum wird nicht durcheinander gebracht.)
Kritixi Lithos
Tatsächlich, wenn Sie die letzte Zeile von /\n/tanach ändern /\n/t, sparen Sie 1 Byte, um 96
Kritixi Lithos
@Cowsquack Danke, 96 ist großartig! Habe jetzt keine Zeit, schau es dir an diesem Wochenende an.
Seshoumara
Klar, schick mir dann einen Ping im Chat :)
Kritixi Lithos
3

Lassen Sie uns über die Befehle tund sprechen T, die zwar in der Manpage erklärt werden, aber leicht zu vergessen sind und versehentlich Fehler verursachen, insbesondere wenn der Code kompliziert wird.

Manpage-Statement für t:

Wenn a s///seit dem Lesen der letzten Eingabezeile und seit dem letzten Befehl t oder T eine erfolgreiche Ersetzung durchgeführt hat, verzweigen Sie zu label.

Beispiel, das zeigt, was ich meine: Nehmen wir an, Sie haben eine Liste mit Zahlen und möchten zählen, wie viele Negative es gibt. Teilcode unten:

1{x;s/.*/0/;x}                   # initialize the counter to 0 in hold space
s/-/&/                           # check if number is negative
t increment_counter              # if so, jump to 'increment_counter' code block
b                                # else, do nothing (start a next cycle)

:increment_counter
#function code here

Sieht ok aus, ist es aber nicht. Wenn die erste Zahl positiv ist, denkt dieser Code immer noch, dass sie negativ war, da der Sprung über tdie erste Eingabezeile unabhängig davon ausgeführt wird, da sbei der Initialisierung des Zählers eine erfolgreiche Substitution stattgefunden hat! Richtig ist: /-/b increment_counter.

Wenn dies einfach erscheint, können Sie sich dennoch täuschen lassen, wenn Sie mehrere Sprünge vor und zurück ausführen, um Funktionen zu simulieren. In unserem Beispiel würde der increment_counterCodeblock sicher viele sBefehle verwenden. Wenn Sie mit zurückkehren, b mainkönnte ein weiterer Check in "main" in dieselbe Falle geraten. Deshalb kehre ich normalerweise von Codeblöcken mit zurück s/.*/&/;t label. Es ist hässlich, aber nützlich.

Seshoumara
quelle
2

s/.*//Verwenden Sie den zBefehl (in Kleinbuchstaben), wenn Sie mit GNU sed arbeiten, anstatt den Musterbereich mit zu löschen. Neben der geringeren Byteanzahl hat dies den Vorteil, dass der nächste Zyklus nicht wie mit dem Befehl gestartet dwird, was in bestimmten Situationen nützlich sein kann.

Seshoumara
quelle
1
Kann auch von Vorteil sein, wenn Sie ungültige Multibyte-Sequenzen haben (die nicht mit übereinstimmen .).
Toby Speight
2

Ich weiß, dass dies ein alter Thread ist, aber ich habe gerade diese plumpen Dezimal-UCD-Konverter mit fast hundert Bytes gefunden, von denen einige sogar den Laderaum durcheinander bringen oder spezielle fehlerhafte sedVersionen erfordern .

Für Dezimalstellen zu UCD ich (68 Bytes; früher am besten hier gepostet 87 Bytes)

s/$/\n9876543210/
:a
s/\([1-9]\)\(.*\n.*\)\1\(.\)/\3x\2\1\3/
ta
P;d

UCD zu Dezimal ist (auch 66 Bytes; früher am besten hier gepostet 96)

s/$/\n0123456789/
:a      
s/\([0-8]\)x\(.*\n.*\)\1\(.\)/\3\2\1\3/
ta      
P;d
  • \nim ersatz ist nicht tragbar. Sie können stattdessen ein anderes Zeichen verwenden und zwei Bytes speichern. Stattdessen benötigen Sie mehr Bytes, um den Anhang zu entfernen P;d. siehe nächste Bemerkung Wenn Ihr Speicherplatz leer ist, verzichten Sie G;s/$/9876543210/auf eine Byte-Strafe.
  • Wenn Sie weitere Verarbeitung benötigen, benötigen Sie einige weitere Bytes für s/\n.*// stattdessenP;d .
  • Für diese fehlerhaften alten GNU- sedVersionen könnten Sie jeweils zwei Bytes einsparen
  • Nein, Sie können diese sechs Backslashes nicht speichern, da erweiterte reguläre Ausdrücke keine Backreferenzen ausführen
Philippos
quelle
In diesem Thread sind keine Konverter für Dezimalzahlen für UCDs und Rückwandler enthalten, die den Speicherplatz durcheinander bringen oder fehlerhafte Sed-Versionen erfordern.
Seshoumara
Ihre eigene Antwort vom 6. April verwendet das goldene Leerzeichen und wird nur mit alten sedVersionen ausgeführt, die gegen den POSIX-Standard verstoßen.
Philippos
Ich mache keine Konvertierungen von Dezimalzahlen in UCDs! Lesen Sie den Thread noch einmal sorgfältig. UCD bedeutet, dass 12 in 0x0xx konvertiert wird (was Ihre Antwort berechnet), während einfach unär (was meine Antwort berechnet) bedeutet, dass 12 in xxxxxxxxxxxx konvertiert wird. Ich habe @ als Symbol gewählt, aber Sie haben die Idee. Bei PPCG muss der POSIX-Standard nicht eingehalten werden.
Seshoumara
Wenn es Ihnen gefällt, Sheriff
Philippos
2

Lesen Sie die gesamte Eingabe auf einmal mit -z

Häufig müssen Sie den gesamten Eingang auf einmal und nicht nur eine Zeile gleichzeitig bearbeiten. Der NBefehl ist dafür nützlich:

:
$!{N;b}

... aber normalerweise kannst du es überspringen und das benutzen -z stattdessen Flagge verwenden.

Das -zFlag setzt sed \0anstelle von NUL ( ) als Trennzeichen für \ndie Eingabezeile ein. Wenn Sie also wissen, dass Ihre Eingabe kein Trennzeichen enthält \0, wird die gesamte Eingabe auf einmal als einzelne „Zeile“ gelesen:

$ echo 'foo
> bar
> baz' | sed -z '1y/ao/eu/'
fuu
ber
bez

Probieren Sie es online!

Jordan
quelle
2

Fügen Sie eine neue Zeile in einem Byte hinzu

Der GBefehl fügt eine neue Zeile und den Inhalt des Speicherbereichs an den Musterbereich an. Wenn der Speicherbereich also leer ist, geschieht Folgendes:

s/$/\n/

Du kannst das:

G

Stellen Sie eine neue Zeile in drei Bytes voran

Der HBefehl fügt eine neue Zeile und den Inhalt des Pattern-Space an den Hold-Space an und xtauscht die beiden aus. Wenn Ihr Hold-Space also leer ist, geschieht dies stattdessen folgendermaßen:

s/^/\n/

Du kannst das:

H;x

Dies verschmutzt Ihren Laderaum und funktioniert nur einmal. Für zwei weitere Bytes könnten Sie jedoch vor dem Auslagern den Musterbereich leeren, was immer noch eine Einsparung von zwei Bytes bedeutet:

H;z;x
Jordan
quelle
1

In sed ist das, was einer Funktion am nächsten kommt, ein Label. Eine Funktion ist nützlich, weil Sie ihren Code mehrmals ausführen können, wodurch viele Bytes gespart werden. In sed müssten Sie jedoch das Rücksendeetikett angeben, und als solches können Sie diese "Funktion" nicht einfach mehrmals im gesamten Code aufrufen, so wie Sie es in anderen Sprachen tun würden.

Die Problemumgehung, die ich verwende, besteht darin, in einen der beiden Speicher ein Flag einzufügen, mit dem das Rückkehretikett ausgewählt wird. Dies funktioniert am besten, wenn der Funktionscode nur einen einzigen Speicherplatz benötigt (den anderen).

Beispiel zeigt, was ich meine: aus einem Projekt von mir genommen, um ein kleines Spiel in sed zu schreiben

# after applying the player's move, I overwrite the pattern space with the flag "P"
s/.*/P/
b check_game_status
:continue_turn_from_player
#code

b calculate_bot_move
:return_bot_move
# here I call the same function 'check_game_status', but with a different flag: "B"
s/.*/B/
b check_game_status
:continue_turn_from_bot
#code (like say 'b update_screen')

:check_game_status   # this needs just the hold space to run
#code
/^P$/b continue_turn_from_player
/^B$/b continue_turn_from_bot

Die Bezeichnungen sollten natürlich nur einen Buchstaben lang sein, ich habe zur besseren Erklärung vollständige Namen verwendet.

Seshoumara
quelle
1

Leere reguläre Ausdrücke entsprechen dem zuvor angetroffenen regulären Ausdruck

(Danke an Riley, der dies aus einer anagolischen Einsendung herausgefunden hat )

Hier ist ein Beispiel, in dem wir die Aufgabe haben, 100 @s in einem leeren Puffer zu erstellen .

s/$/@@@@@@@@@@/;s/.*/&&&&&&&&&&/ # 31 bytes
s/.*/@@@@@@@@@@/;s//&&&&&&&&&&/  # 30 bytes

Die zweite Lösung ist 1 Byte kürzer und verwendet die Tatsache, dass leere reguläre Ausdrücke mit dem zuletzt gefundenen regulären Ausdruck gefüllt werden. Hier war für die zweite Substitution die letzte Regex .*, daher wird die leere Regex hier mit gefüllt .*. Dies funktioniert auch mit regulären Ausdrücken in /conditionals/.

Beachten Sie, dass es sich um den zuvor angetroffenen regulären Ausdruck handelt, sodass auch der folgende funktioniert.

s/.*/@@@@@@@@@@/;/@*/!s/$/@/;s//&&&&&&&&&&/

Der leere Regex wird gefüllt, @*anstatt $dass er s/$/@/nie erreicht wird.

Kritixi Lithos
quelle
Ja, gute Antwort. Ich habe sogar reguläre Ausdrücke länger gemacht, damit sie so angepasst werden können (wodurch das Programm kürzer wird).
Toby Speight
0

Meist nutzloser Schritt:

y|A-y|B-z|

Dies wird nur Azu Bund yzu z(... und -zu -;) übersetzen, aber sonst nichts

sed -e 'y|A-y|B-z|' <<<'Hello world!'

Ich komme gerade zurück:

Hello world!

Sie könnten dies sicherzustellen nutzlos sein wird, für die Probe durch diese Verwendung auf Klein hexadezimalen Werten (mit nur 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, eoder f.)

F. Hauri
quelle
2
Hast du das auf die harte Tour herausgefunden ?! ;-)
Toby Speight
Ich mag nutzlose Skripte: sed '; ;/s/b;y|A-y|B-z|;s ;s/ //; ; ;' <<<'Hello world'(Warum wird das Leerzeichen nicht unterdrückt?)
F. Hauri