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



はありません。



All Articles