ZSH:ファッキーニューイヤー

私は投稿habrahabr.ru/post/247161を読んで考えました:ここで、男が「Happy new year」を表示する理解できないプログラムをbashで書いた。 しかし、これはbashです! zshが悪化していないことを示す必要がありますが、はるかに優れています! そして、「Happy New Year!」(ロシア語で!)を表示するzsh上のプログラムは、次の制限付きで:
  1. プログラムは、サードパーティのプログラムを使用しないでください。 base64でもcatでもない。
  2. プログラムはテキストをロシア語で表示する必要があります。
  3. プログラムはASCIIで作成する必要がありますが、単一の文字または数字を含めることはできません。
bashをどのように処理するかわかりませんが、zshを使用するとすべてが簡単になります。

Zshには変数を展開するためのパラメーターがあります( man zshexpnからのパラメーター展開フラグ): echo ${(#):-65}はラテン文字「A」を表示します。 現在のロケールで動作します。 原則として、これで目的のプログラムを作成するには十分ですが、人生を大いに促進する他の知識があります。

まず、匿名関数があるため、関数の名前を作成する必要はありません(ただし、関数を+で呼び出すこともできます)。また、追加の@配列(1つではなく、1つのスコープ内の1つのみ)を取得することもできます。

第二に、zshが数値を期待する場合はいつでも、算術展開(同じman zshexpnからの算術展開、 man zshexpn算術評価セクションで詳しく説明)を使用できます。これにより、 $(())$[] 、yesそしてちょうど$ 。 上記の例を含めて、 V=0x41; echo ${(#):-V+(VV)}書くことができV=0x41; echo ${(#):-V+(VV)} V=0x41; echo ${(#):-V+(VV)} 、インデックス内の値に適用されます( $@を使用する場合に便利です)。

3番目に、配列を使用して${(#)}ような手順を実行でき、配列の各要素に(#)適用されます。

4番目に、複数の変換を連続して適用する必要がある場合、一時変数は必要ありません。 : (#)およびスペースの削除)。 文字列を変換するための一時変数も必要ありません: ${:-string}stringに展開されstring 、ここには変数はありません(上記で使用したオプション)。 Bash、および一般的に他のすべてのシェルは、これを行うことができません。

したがって、次のコードを取得します。
  1 (){__=$# } !;___=$[__<<__];____=$[__<<___];_____=$((___<<___)) 2 _______=$((__+(__<<(__+____))+(__<<(__+____))<<(__+____))) 3 ______=$((_______+__+__<<____)) 4 \*(){(( ${@[-__]} < ______+(______-_______)+_____ )) && { <<< $@ ; <<< $(\* $[$@+____]) } } 5 +(){<<< "${${(#)@}// }"} 6 (){ 7 (){<<< "$@"} \ 8 "$(+ _______)" \ 9 "$(+ ${@[____]}-__ ${@[____]} ______ ${@[-__]}+__ ${@[____]}-___)" \ 10 "$(+ ${@[__]}+__ ${@[____]} ${@[__]}+___ ${@[____]} ${@[____]}-___ '____<<(____-__)+__')" \ 11 } $(\* $[______]) 
。 ここで、最初の行では値1、2、4、8で変数を宣言し、2番目では値0x0421(U + 0421はCYRILLIC CAPITAL LETTER ES)、3番目では-0x0432(CYRILLIC SMALL LETTER VE)で宣言します。 4行目では、4の増分で数値のシーケンスを生成する再帰関数; 5行目では、上記でほぼ考慮した関数で、算術式の配列をスペースなしの文字列に変換します。

6行目の匿名関数は、4行目の関数で生成された数値を7行目の配列に関連付けるために必要です。複数の行をスペースで区切って1つに結合します。 11行目では、再帰ジェネレーターが呼び出され、残りにはテキスト自体が含まれています。

問題は解決されたようです。 以下を開始します。
 env -i PATH= LANG=ru_RU.UTF-8 /bin/zsh -f script.zsh *: command not found: cat *: command not found: cat *: command not found: cat *: command not found: cat *: command not found: cat *: command not found: cat *: command not found: cat *: command not found: cat *: command not found: cat *: command not found: cat *: command not found: cat *: command not found: cat *: command not found: cat *: command not found: cat +: command not found: cat +: command not found: cat +: command not found: cat (anon): command not found: cat 
。 ああ、ここで何かが間違っています。最初の条件に違反しています。 それはすべて$NULLCMDについて$NULLCMD :コマンドがなく、リダイレクトを使用する場合、この変数の値は暗黙的に置換され、デフォルトではcat等しくなります。 解決策は、 cat関数を作成することです。
 cat() while { read i } { echo $i } 
(はい、中括弧のない関数の定義は正しいです。ループの場合もdoが、 do / done do )。 これはevalなしでは実行できないため、実行する行はeval 'cat()while {read i} {echo $i}'の形式である必要があります。 ここでの小さな問題は、 <<<を使用できないため、気にすることはできず、 echoを含む/を返す別の変数または関数ですべてを単純に書き換えることです。 最終プログラムでは、これは@関数です。
 (){__=$# } !;___=$[__<<__];____=$[__<<___];_____=$((___<<___)) ______=$[_____<<____-___<<____+____] ________="${(#):-______+__}${(#):-______-__}${(#):-______+____}${(#):-______+_____+___+__}" @() $________ $________ _______=$((__+(__<<(__+____))+(__<<(__+____))<<(__+____))) ______=$((_______+__+__<<____)) \*(){(( ${@[-__]} < ______+(______-_______)+_____ )) && { $(@) $@ ; $(@) $(\* $[$@+____]) } } +() $(@) "${${(#)@}// }" (){ (){$(@) "$@"} \ "$(+ _______)" \ "$(+ ${@[____]}-__ ${@[____]} ______ ${@[-__]}+__ ${@[____]}-___)" \ "$(+ ${@[__]}+__ ${@[____]} ${@[__]}+___ ${@[____]} ${@[____]}-___ '____<<(____-__)+__')" } $(\* $[______]) 
最後から2番目のステップは、引用符を取り除くことです。
 (){__=$# } !;___=$[__<<__];____=$[__<<___];_____=$((___<<___)) ______=$[_____<<____-___<<____+____] ________=${(#):-______+__}${(#):-______-__}${(#):-______+____}${(#):-______+_____+___+__} @() $________ $________ _______=$((__+(__<<(__+____))+(__<<(__+____))<<(__+____))) ______=$((_______+__+__<<____)) \*(){(( ${@[-__]} < ______+(______-_______)+_____ )) && { $(@) $@ ; $(@) $(\* $[$@+____]) } } +() $(@) "${${(#)@}// }" (){ (){$(@) $@} \ $(+ _______) \ $(+ ${@[____]}-__ ${@[____]} ______ ${@[-__]}+__ ${@[____]}-___) \ $(+ ${@[__]}+__ ${@[____]} ${@[__]}+___ ${@[____]} ${@[____]}-___ '____<<(____-__)+__') } $(\* $[______]) 
少し縮小しますが、何かコードが痛々しいほど明確です:
 (){__=$# } !;___=$[__<<__];____=$[__<<___];_____=$[___<<___] ______=$[_____<<____-___<<____+____] ________=${(#):-______+__}${(#):-______-__}${(#):-______+____}${(#):-______+_____+___+__} @()$________ $________ _______=$[__+(__<<(__+____))+(__<<(__+____))<<(__+____)] ______=$[_______+__+__<<____] \*(){((${@[-__]}<______+(______-_______)+_____))&&{`@` $@ `\* $[$@+____]` }} +()`@` "${${(#)@}// }" (){(){`@` $@} `+ _______` `+ ${@[____]}-__ ${@[____]} ______ ${@[-__]}+__ ${@[____]}-___` `+ ${@[__]}+__ ${@[____]} ${@[__]}+___ ${@[____]} ${@[____]}-___ '____<<(____-__)+__'` } `\* $[______]` 
env -i PATH= LANG=ru_RU.UTF-8 /bin/zsh -f script.zshます。

Unicodeロケールを必要としないオプション(行eval LANG=Cがそこに収集され、このロケールでは${(#)}は指定された値のバイトを生成します):
 (){__=$# } !;___=$[__<<__];____=$[__<<___];_____=$[___<<___] ______=$[_____<<____-___<<____+____] ________=${(#):-______+__}${(#):-______-__}${(#):-______+____}${(#):-______+_____+___+__} @()$________ $________ ^()`@` ${(#):-$@[__]}${(#):-$@[___]} ''(){(($@<(_____<<____)))&&`@` ${(#)@}||^ $[($@)>>(____+___)|(_____<<____+_____<<(___+__))] $[($@)&(__<<(____+___)-__)|(_____<<____)]} _______=$[__+(__<<(__+____))+(__<<(__+____))<<(__+____)] ______=$[_______+__+__<<____] \*(){((${@[-__]}<${@[__]}))&&{`@` $[$@[-__]] `\* $@[__] $[$@[___]+____]` }} +(){(($#))&&`@` $('' $@[__])$(+ $@[___,-__])} /()`@` "${${(#)@}// }" `() {/ $@[__] $@[-__]+____+__ $@[__]-____ $@[-___]-__} $(\* $[#________+(_____<<__)] $[#________])` \ `() {/ $@[-__] $@[__]+__ $@[-__]+___ $@[___+__]-__} $(\* '(____<<____)+_____*___' '____<<____')`=${(#):-$[_____<<(___+__)+___+__]} (){(){`@` $@} `+ _______` `+ ${@[____]}-__ ${@[____]} ______ ${@[-__]}+__ ${@[____]}-___` `+ ${@[__]}+__ ${@[____]} ${@[__]}+___ ${@[____]} ${@[____]}-___ '____<<(____-__)+__'` } `\* '______+(______-_______)+_____' ______` 


「フォークなし」と「アンダースコアなし」という制限を追加すると、タスクはより興味深いものになります。 この場合、いくつかのトリックを覚えておく必要があります。まず、不明なコマンドを実行しようとすると、リターンコード127で終了し、変数$?表示されます$?&>‐これはエラーメッセージの出力を防ぎます:関数/()+++++++&>- +++++++コマンドがない限り、 ?=127 /()+++++++&>-同等?=127 (もちろん、この変数に割り当てることができる場合)。 +++++++${#?}は3になります。 /()''&>-使用することもできます:このオプションは常に$? 126提供します$? 、ディレクトリを実行できないため(注:空の名前で関数を定義できます。これにより、このトリックを使用できなくなります)。 最後のトリック:関数/().&>- $? /().&>-割り当て$? 1を呼び出す試みがあるため. 引数なし、および/(). ''&>- /(). ''&>- 、なぜなら . $PATHで空の名前のファイルを見つけようとしますが、もちろん、 $PATHにディレクトリの代わりにファイルを追加すると、「これはディレクトリではありません」というメッセージで同じ戻りコードを取得します: path_item/ 、およびpath_item/の存在を探します最後にこの結果につながります)。

第二に、変数$! バックグラウンドで実行されている最後のプロセスのPIDが含まれます。 または、バックグラウンドで何も開始されなかった場合は0(最後の部分はドキュメントに記載されていません)。

第三に、私は答える記事でこのトリックを使用しましたが、まだ使用していません。単純に文字列を連結することで数値を収集できます。 これらを${base}#${number}の形式で収集することもできます。これにより、タスクは生成のために単純になります: $! 0があり、 $@には1つの要素(1)の配列があり、 $[$@+$@]#$@$!$!$!の形式で番号を収集し$[$@+$@]#$@$!$!$! 2#1000または8を収集します。 何らかの理由で生成したくない場合は、必要な値を$@に割り当てる機能を備えた匿名関数が機能します。 eval LANG=Cを実行するコードの例を次に示します。
 /()+++&>- //()''&>- ///().&>- () { () { / () { / () { / ${(#):-$@[${##}<<${##}]+${##}}${(#):-$@[$#]+${##}<<${#?}}${(#):-$@[${#}-${##}]-${#?}}${(#):-$@[$#]-${##}-${##}} $@[${##}] } $@[${##}] ${##}$!$! $[${##}$!$!+${##}$!] } ${(#):-$@[$#]+${?[-${##}]}-${##}}${(#):-$@[${#}-${##}]+${?[${##}]}+${##}<<${?[${##}+${##}]}}${(#):-$@[$#]+${##}<<${#?}}${(#):-$@[$#]+${##}}=${(#):-$@[$#]-${#?}} $@[${##},${#?}] } $@ $[$@[$#]+${#}-${##}]$! $[$@[$#]+${#}]$! } ${#?} $[${#?}+${#?}] $[${#?}<<(${#?}+${#?})] 


Pythonのワンライナーの例を次に示します。これは、上記のテクノロジーを使用して、 echo !を収集しecho !
 #!/usr/bin/env python3.4 print(''.join( ( ' ' if i == next(iter(b' ')) else '${{(#):-$[$[$@+$@]#{:b}]}}'.format(i).replace('0', '$!').replace('1', '$@') ) for i in 'echo   !'.encode('utf8'))) 


echo ! および手動アセンブリeval LANG=C
 /()+++&>- //()''&>- ///(). ''&>- () { () { / () { / () { / ${(#):-$@[${##}<<${##}]+${##}}${(#):-$@[$#]+${##}<<${#?}}${(#):-$@[${#}-${##}]-${#?}}${(#):-$@[$#]-${##}-${##}} $@[${##}] } $@[${##}] ${##}$!$! $[${##}$!$!+${##}$!] } ${(#):-$@[$#]+${?[-${##}]}-${##}}${(#):-$@[${#}-${##}]+${?[${##}]}+${##}<<${?[${##}+${##}]}}${(#):-$@[$#]+${##}<<${#?}}${(#):-$@[$#]+${##}}=${(#):-$@[$#]-${#?}} $@[${##},${#?}] } $@ $[$@[$#]+${#}-${##}]$! $[$@[$#]+${#}]$! } ${#?} $[${#?}+${#?}] $[${#?}<<(${#?}+${#?})] : () { ${(#):-$[$[$@+$@]#$@$@$!$!$@$!$@]}${(#):-$[$[$@+$@]#$@$@$!$!$!$@$@]}${(#):-$[$[$@+$@]#$@$@$!$@$!$!$!]}${(#):-$[$[$@+$@]#$@$@$!$@$@$@$@]} ${(#):-$[$[$@+$@]#$@$@$!$@$!$!$!$!]}${(#):-$[$[$@+$@]#$@$!$@$!$!$!$!$@]} ${(#):-$[$[$@+$@]#$@$@$!$@$!$!$!$!]}${(#):-$[$[$@+$@]#$@$!$@$@$@$@$!$@]}${(#):-$[$[$@+$@]#$@$@$!$@$!$!$!$!]}${(#):-$[$[$@+$@]#$@$!$@$@$@$@$@$!]}${(#):-$[$[$@+$@]#$@$@$!$@$!$!$!$!]}${(#):-$[$[$@+$@]#$@$!$@$@$!$!$@$!]}${(#):-$[$[$@+$@]#$@$@$!$@$!$!$!$@]}${(#):-$[$[$@+$@]#$@$!$!$!$@$!$@$@]}${(#):-$[$[$@+$@]#$@$@$!$@$!$!$!$!]}${(#):-$[$[$@+$@]#$@$!$@$@$@$@$!$!]} ${(#):-$[$[$@+$@]#$@$@$!$@$!$!$!$!]}${(#):-$[$[$@+$@]#$@$!$@$@$!$!$@$@]}${(#):-$[$[$@+$@]#$@$@$!$@$!$!$!$!]}${(#):-$[$[$@+$@]#$@$!$@$@$@$@$@$!]}${(#):-$[$[$@+$@]#$@$@$!$@$!$!$!$!]}${(#):-$[$[$@+$@]#$@$!$@$@$!$@$!$!]}${(#):-$[$[$@+$@]#$@$@$!$@$!$!$!$!]}${(#):-$[$[$@+$@]#$@$!$@$@$@$@$@$!]}${(#):-$[$[$@+$@]#$@$@$!$@$!$!$!$!]}${(#):-$[$[$@+$@]#$@$!$@$@$@$@$!$!]}${(#):-$[$[$@+$@]#$@$!$!$!$!$@]} } ${#?} 

: $? 0が含まれることを保証するために必要$?straceを実行すると、まだforkが残っていることがわかります。起動時に、 forkが最初に作成され、次に+++可能な場所がソートされます。 ''同じことが起こるので、関数////で置き換える必要があります。これは///ではなく)とまったく同じ結果になりますが、 forkはありません。

Source: https://habr.com/ru/post/J247249/


All Articles