シェル芸クワインの作り方


自己紹介

@yasuhiroki
tw: @duck_yasuhiroki

シェル芸術<クワイン編>

そのシェルスクリプトもうちょっとシンプルに書けそう Tips集(Golf/シェル芸ではない)


シェル芸とは

マウスも使わず、ソースコードも残さず、GUIツールを立ち上げる間もなく、あらゆる調査・計算・テキスト処理をCLI端末へのコマンド入力一撃で終わらすこと。あるいはそのときのコマンド入力のこと。

シェル芸の定義バージョン1.11


クワインとは

コンピュータプログラムの一種で、自身のソースコードと完全に同じ文字列を出力するプログラムである。2


シェル芸クワインとは

シェル芸であり、クワインである

$ echo $BASH_COMMAND
echo $BASH_COMMAND

$ echo $BASH_COMMAND | bash
echo $BASH_COMMAND

$ echo $BASH_COMMAND | bash | bash
echo $BASH_COMMAND

シェル芸クワインの実装例


ヒルベルト曲線シェル芸クワイン (前回発表)

n= h='l=\47L${r}FR${l}F${l}RF${r}L\47 r=\47R${l}FL${r}F${r}LF${l}R\47 %s l= r= eval echo \47$l\47 | { read a; b=${a%%%%F*}; echo "import sys;from turtle import *;speed(0);pensize(2);ms=min(screensize())*0.8;l=2*ms/(2**${#b}-1);up();setpos(-ms,-ms);down();${a}sys.exit()"; } | sed \47s/L/lt(90);/g;s/R/rt(90);/g;s/F/fd(l);/g\47 | python \n' s='n=\47%s\47 h=%s s=%s eval $\47printf "${h}${s}" "${n} eval"{,} "${h@Q}" "${s@Q}"\47' eval $'printf "${h}${s}" "eval ${n}"{,} "${h@Q}" "${s@Q}"'

山手線内回りシェル芸クワイン

n=0 y='5p2x5LqsIOaciealveeUuiDmlrDmqYsg5rWc5p2+55S6IOeUsOeUuiDlk4Hlt50g5aSn5bSOIOS6lOWPjeeUsCDnm67pu5Ig5oG15q+U5a+/IOa4i+iwtyDljp/lrr8g5Luj44CF5pyoIOaWsOWuvyDmlrDlpKfkuYXkv50g6auY55Sw6aas5aC0IOebrueZvSDmsaDooosg5aSn5aGaIOW3o+m0qCDpp5Lovrwg55Sw56uvIOilv+aXpeaarumHjCDml6Xmmq7ph4wg6bav6LC3IOS4iumHjiDlvqHlvpLnlLog56eL6JGJ5Y6fIOelnueUsAo=' l='convert -pointsize $(($(tput cols)/4)) -font ArialUnicode label:%s pbm:- | pbmtoascii 1>&2\n' s='n=%d y=%s l=%s s=%s eval $\47printf "${l}${s}\n" "$(echo ${y}|base64 --decode|xargs -n1|sed -n $((n%%29+1))p)" "$((n+1))" "${y@Q}" "${l@Q}" "${s@Q}"\47' eval $'printf "${l}${s}\n" "$(echo ${y}|base64 --decode|xargs -n1|sed -n $((n%29+1))p)" "$((n+1))" "${y@Q}" "${l@Q}" "${s@Q}"'

山手線内回りシェル芸クワイン

次は東京ー東京ー


山手線内回りシェル芸クワイン

東京の次はー有楽町ー


山手線内回りシェル芸クワイン

フォントサイズを小さくすれば鮮明な文字に


山手線内回りシェル芸クワイン

無事一周


シェル芸クワインのための必須技術


シェル芸クワインのための必須技術

  • s=1 echo ${s} では ${s} の値は出力されない
    • s=1 eval 'echo ${s}' なら ${s} の値は出力される
    • eval により s=1 が評価されてから echo ${s} が実行されるため

シェル芸クワインのための必須技術

  • s="%s" eval 'printf "${s}" "hoge"'hoge が出力される
    1. s="%s" eval 'printf "${s}" "hoge"'
    2. eval 'printf "${s}" "hoge"'
      (s="%s" が定義済みな状態)
    3. printf "${s}" "hoge"
    4. printf "%s" "hoge"

シェル芸クワインの基本形

比較的シンプルなクワインシェル芸の例

  • 全く同じコードが含まれている
    • クォートをエスケープしているか、していないかだけ
  • エスケープと %s の位置さえ間違えなければカスタムできる

↓コピペ用

s='s=%s eval $\47printf "${s}" "${s@Q}"\47' eval $'printf "${s}" "${s@Q}"'

シェル芸クワインの発展形

  • 変数を増やした例
    • n=%d により動的に n の値を変更できる
    • $((++n)) にしているが、別に ${RANDOM} などでもよい
      • 同じコードを2箇所に追加することさえ忘れなければOK
      • ただし空白や改行やクォートが混ざるとエスケープが難しいので注意
      • % も混ざると大変

↓コピペ用

n=1 s='n=%d s=%s eval $\47printf "${s}" "$((++n))" "${s@Q}"\47' eval $'printf "${s}" "$((++n))" "${s@Q}"'

シェル芸クワインのデバッグ方法

  • bash -xv で地道に頑張る
  • 慣れてくると目で気付く(末期)

山手線内回りシェル芸クワインの解説

使用しているコマンド

  • base64 コマンド
    • 駅名を base64 でエンコードしている
    • 別に不要なのだけど 東京 有楽町 ... と文字列があるとネタバレになるので隠したかった
  • tput コマンド
    • ターミナルの横幅を取得する

山手線内回りシェル芸クワインの解説

使用しているコマンド

# 漢字を表示できるフォントを探すシェル芸
$ convert -list font | grep Font: | awk '{print $2}' | xargs -I@ echo 'echo @; convert -pointsize 20 -font @ label:漢字OK pbm:- | pbmtoascii' | bash

山手線内回りシェル芸クワインの解説


m=60 n=-1 l='echo "$(yes @ | head -n%d | xargs | tr -d @)^" 1>&2\n' l2='echo -e "$(yes @ | head -n%d | xargs | tr -d @)/$(yes @ | head -n%d | xargs | tr @ " ")\\\\" 1>&2\n' s='m=60 n=%d l=%s s=%s eval $\47printf "${l}${s}" "$((m-n-2))" "$((n+1))" "$((n+1))" "${l@Q}" "${s@Q}"\47' eval $'printf "${l}${s}" "$((m-n-2))" "$((n+1))" "${l2@Q}" "${s@Q}"'

おしまい