zsh, 603 594 566 561 548 440 415 399 378 370 байт
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;}
Зависит от coreutils + dc
.
Попробуйте онлайн!
Это было ... путешествие.
Этот ответ состоит из трех частей. Первые 4 строки обрабатывают определенные особые случаи, чтобы упростить следующий код. Следующие 2 строки и последняя строка, по сути, выполняют одно и то же, но ровно одна запускается с удалением любого символа. Они написаны в основном с дополнительными наборами символов, так что удаление любого символа нарушает только один, позволяя другому продолжать функционировать.
Глядя на первую часть, мы сначала справимся
- удаление новой строки с
ec\nho \\n
- удаление пробела с помощью
ca t<<<$'\x20'
(с последующим, exi t
чтобы избежать запуска более позднего кода, который привел бы к постороннему выводу)
$
удаление с d$c -e8BC6P
( 8BC6
= 9226
is 36*256 + 10
, а 36 и 10 - байтовые значения $
символов и новой строки соответственно; мы используем шестнадцатеричные цифры в десятичном формате, чтобы избежать необходимости включать их в большой комментарий в строке 6)
0
удаление с d0c -eKp
( K
получает десятичную точность, которая 0
по умолчанию)
В следующей части используются только символы (кроме мусора в конце второй строки) $'\01234567v;
, пробел и перевод строки. Из них четыре были учтены, поэтому остаток ( '\1234567v
) не может быть в последней строке. Развернув восьмеричные экранированные $'\123'
символы ( представляет символ ASCII со значением 123 8 ), мы получим:
zsh -c 'for v in {#..\}};{ fgrep -q $v '$0'||<<<$v;}'
eval ':;:&&exit'
Первая строка перебирает все символы, используемые в программе, и ищет каждый из них в своем собственном исходном коде ( $0
это имя файла выполняемого скрипта), печатая любой символ, который не найден.
Вторая строка выглядит немного странно и, кажется, делает то же самое, что и exit
с кучей nops. Однако кодирование exit
как восьмеричное напрямую приводит к тому $'\145\170\151\164'
, что не содержит 2
или 3
. Нам на самом деле нужно сделать это менее устойчивым к удалению. Это потому, что если какой-либо из '\014567v
них удаляется, ломая первую строку, вторая строка также разрывается, что позволяет выполнить оставшуюся часть кода. Однако нам нужно, чтобы он также ломался, если 2
или 3
удаляется, чтобы строки 3 и 4 могли работать. Это достигается путем подковывания в :
и ;
, которые имеют 2 и 3 в восьмеричном представлении соответственно.
Мусор в конце строки 2 просто необходим для того, чтобы каждый печатный символ ASCII появлялся хотя бы один раз, так как для этого требуется проверка, циклически проходящая по каждому из них.
Если exit
не был вызван в первом разделе (то есть он был поврежден путем удаления одного из '\01234567v
), мы переходим ко второму, в котором мы должны выполнить то же самое, не используя ни один из этих символов. Последняя строка аналогична декодированной первой строке, за исключением того, что мы можем сократить диапазон цикла, чтобы сохранить несколько байтов, поскольку мы уже знаем, что все символы, кроме '\01234567v
, были охвачены. Он также имеет 0# $#
перед ним, который комментирует его и не дает ему производить посторонний вывод, если 0
или $
были удалены.