zsh, 603 594 566 561 548 440 415 399 378 370 Bytes
ec
ho \\n;ca t<<<$'\x20';exi t
d$c -e8BC6P
d0c -eKp
$'\172\163\150' $'\055\143' $'\146\157\162 v \151\156 \173\043\056\056\134\175\175\073\173 \146\147\162\145\160 \055\161 $\166 '$0$'\174\174\074\074\074$\166\073\175'
$'\145v\141\154' $':\073\072\046\046\145\170\151\164';#%&()*+,/9=>?@ADEFGHIJLMNOQRSTUVWXYZ[]^_`jklmsuwy
0# $#;for b in {$..z};{ fgrep -q $b $0||<<<$b;}
Hängt von coreutils + ab dc
.
Probieren Sie es online!
Das war ... eine Reise.
Diese Antwort besteht aus drei Teilen. Die ersten 4 Zeilen behandeln bestimmte Sonderfälle, um den folgenden Code zu vereinfachen. Die nächsten beiden Zeilen und die letzte Zeile bewirken im Wesentlichen dasselbe, aber genau eine Zeile wird mit einer bestimmten Zeichenentfernung ausgeführt. Sie sind mit meist komplementären Zeichensätzen geschrieben, sodass das Entfernen von Zeichen höchstens einen Zeichensatz unterbricht und der andere weiterhin funktioniert.
Mit Blick auf den ersten Teil beschäftigen wir uns zuerst
- Zeilenumbruch mit
ec\nho \\n
- Leerzeichen entfernen mit
ca t<<<$'\x20'
(gefolgt von exi t
, um zu vermeiden, dass späterer Code ausgeführt wird, was zu einer überflüssigen Ausgabe führen würde)
$
Entfernung mit d$c -e8BC6P
( 8BC6
= 9226
ist 36*256 + 10
, und 36 und 10 sind die Bytewerte der $
Zeichen und der Zeilenumbruchzeichen; wir verwenden hexadezimale Ziffern, um zu vermeiden, dass sie in den großen Kommentar in Zeile 6 aufgenommen werden müssen.)
0
Entfernen mit d0c -eKp
( K
erhält die 0
Standard- Dezimalgenauigkeit )
Im nächsten Teil werden als einzige Zeichen (außer dem Müll am Ende der zweiten Zeile) $'\01234567v;
Leerzeichen und Zeilenumbrüche verwendet. Davon wurden vier berücksichtigt, sodass der Rest ( '\1234567v
) nicht in der letzten Zeile vorkommen kann. Wenn Sie die oktalen Escapes erweitern ( $'\123'
entspricht dem ASCII-Zeichen mit dem Wert 123 8 ), erhalten Sie:
zsh -c 'for v in {#..\}};{ fgrep -q $v '$0'||<<<$v;}'
eval ':;:&&exit'
Die erste Zeile durchläuft alle im Programm verwendeten Zeichen und sucht nach jedem Zeichen im eigenen Quellcode ( $0
ist der Dateiname des ausgeführten Skripts). Dabei werden alle Zeichen ausgegeben, die nicht gefunden wurden.
Die zweite Zeile sieht ein wenig seltsam aus und scheint dasselbe zu tun wie exit
mit ein paar Nops. Die Codierung exit
als Oktal führt jedoch direkt dazu $'\145\170\151\164'
, dass 2
oder nicht enthalten ist 3
. Wir müssen dies tatsächlich weniger widerstandsfähig gegenüber Umzügen machen. Dies liegt daran, dass, wenn eines von '\014567v
entfernt wird, die erste und die zweite Zeile unterbrochen werden, so dass der Rest des Codes ausgeführt werden kann. Es muss jedoch auch unterbrochen werden, wenn 2
oder 3
entfernt werden, damit die Zeilen 3 und 4 ausgeführt werden können. Dies wird durch Einblenden von :
und erreicht ;
, die eine 2 bzw. 3 in ihrer Oktaldarstellung haben.
Der Müll am Ende von Zeile 2 ist einfach da, um sicherzustellen, dass jedes druckbare ASCII-Zeichen mindestens einmal vorkommt, da dies für die Art und Weise, wie die Prüfung durch Durchlaufen der einzelnen Zeichen erfolgt, erforderlich ist.
Wenn exit
es im ersten Abschnitt nicht aufgerufen wurde (dh durch das Entfernen eines der '\01234567v
Zeichen beschädigt wurde ), fahren wir mit dem zweiten fort, in dem wir dasselbe tun müssen, ohne eines dieser Zeichen zu verwenden. Die letzte Zeile ähnelt der decodierten ersten Zeile, mit der Ausnahme, dass wir den Bereich der Schleife verkleinern können, um einige Bytes zu sparen, da wir bereits wissen, dass alle Zeichen mit Ausnahme von '\01234567v
abgedeckt wurden. Es hat auch 0# $#
vor sich, was es auskommentiert und verhindert, dass es eine fremde Ausgabe erzeugt, wenn 0
oder $
entfernt wurden.