12
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-07-01
1 / 27

シェル芸術とは

  • さっき思いついた1
  • 読み方(案)
    • ShellGeiJutsu
    • ShellGei-no-Jutsu
  • 意味
    • シェル芸の術
    • シェルの芸術
    • 本当は、とくに意味はない

クワインとは

  • クワイン(Quine)
    • 自身のソースコードと完全に同じ文字列を出力するプログラム。2

簡単なクワインの例

    • bash $BASH_COMMAND
    $ echo $BASH_COMMAND
    echo $BASH_COMMAND
    
    $ echo $BASH_COMMAND | bash
    echo $BASH_COMMAND
    

以降 Bash 4.4 での作業とする


先行例

s='s=\47%s\47;printf "${s}" "${s}"';printf "${s}" "${s}"

http://cs.lmu.edu/~ray/notes/quineprograms/


本当にクワインか確認する

$ eval cat q.sh "| bash"{,,,,,}

jot コマンドが使えるなら

$ eval cat q.sh "$(jot -b '|bash' -s ' ' 30)"

先行例の解説

s='s=\47%s\47;printf "${s}" "${s}"';printf "${s}" "${s}"

s='s=\47%s\47;printf "${s}" "${s}"'
printf "${s}" "${s}"

printf \
's=\47%s\47;printf "${s}" "${s}"' \
's=\47%s\47;printf "${s}" "${s}"'

printf \
's=\47s=\\47%%s\\47;printf "${s}" "${s}"\47;printf "${s}" "${s}"'

(私が勝手に感じる)課題

  • ; を使っている
    • 実質2行のBash
    • ワンライナーとは言い難い(個人の見解
  • 視覚的にものたりない
    • ヒルベルト曲線とは言わないけど何かしたい

ワンライナーへの挑戦

変数の定義と参照をワンライナーで行うには

$ s='hoge' printf "$s"
  • これでは何も出力されない
  • printf "$s"$s が展開されるタイミングではまだ値が入っていない

eval の活用

$ s='hoge' eval 'printf "$s\n"'
hoge

eval を使うことで $s を展開するタイミングを遅らせられる
詳しくは「ヒルベルト曲線 + シェル芸」で検索 :mag:


先行例を修正

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

遊びやすくする

Howto本 3 によれば 「空白」 を減らすことが望ましいとのこと(意訳)


空白を減らす

空白をアスキーコードにする

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

最後の printf 内の空白を \40 にしても動く。
ただしクワインにはならない。


長くなったので少しだけ短くする

${val@Q} を活用

s='s=%s\40eval\40$\47printf\40"${s}"\40"${s@Q}"\47' eval $'printf "${s}" "${s@Q}"'
  • ${val@Q} の説明は後述

空白が無ければ遊べる

直角に曲げてみる

s='s
=%s\
40ev
al\4
0$\4
7printf\40"${s//\n/}"\40"${s@Q}"\47
' eval $'printf "${s//\n/}" "${s@Q}"'
  • ${s//\n/} で改行を消しているのがミソ
  • :sweat_smile: クワインではない
    • クワインを出力するスクリプトではあるが...
    • 改善したい

アスキーアート

s='s
=   %
s\40
e   v
al\40

  $
 \ 4
7   p
rintf
\   4

 0"${
s
 //[\
     n
\40]/}"

\    4
0    "
${s@Q}
"    \
4    7' eval $'printf "${s//[\40\n]/}" "${s@Q}"'

アスキーアート 2

s='     s    =%s\ 4   0
e  v   a l  \     4   0
$\47  p   r  intf \40"$
{   s //[\4     0 \   n
]}"\  4   0 "${s  @   Q

}"\47' eval $'printf "${s//[\40\n]}" "${s@Q}"'

状態を持つクワイン

実行する度に n が加算されるクワインシェル芸

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

レベルが上がるクワイン

実行する度にレベルアップするクワインシェル芸

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

↓ ちょっと改行して見やすく

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

レベルが残るクワイン

n=1 l='echo レベル%d 1>&2\n' s='n=%d l=%s s=%s eval $\47printf "${l}${s}" "${n}" "$((n+1))" "${l@Q}" "${s@Q}"\47' eval $'printf "${l}${s}" "${n}" "$((n+1))" "${l@Q}" "${s@Q}"'
  • 標準出力だけ見ればクワイン

ヒルベルト曲線クワイン

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}"'
  • 鳥海さんのヒルベルト曲線をクワイン化
    • :warning: 厳密にはクワインではない
    • 2度目の実行以降がクワイン化されている
      • これは、クワインシェル芸を作るシェル芸

おまけ

${parameter@operator}

Bashの機能 (zshにはなかった)

  • ${parameter@A}
    • 定義時の文字列を出力
  • ${parameter@E}
    • echo -e$'' と同じ
  • ${parameter@P}
    • prompt string を変換する
  • ${parameter@Q}
    • 値をクォートして出力

おまけ

${parameter@operator}

val="hoge\tfuga"
echo "${val}"
echo -e "${val}"
echo "${val@A}"
echo "${val@E}"
echo "${val@P}"
echo "${val@Q}"
hoge\tfuga
hoge    fuga
val='hoge\tfuga'
hoge    fuga
hoge22:52:26fuga
'hoge\tfuga'

クワインの確認の限界(?)

$ eval cat q.sh "$(jot -b '|bash' -s ' ' 244)"
echo $BASH_COMMAND

$ eval cat q.sh "$(jot -b '|bash' -s ' ' 245)"
bash: 1 行: echo: 書き込みエラー: Bad file descriptor

パイプをつなぐ数の限界?


おしまい


  1. すでに誰かが言ってそうな気がする

  2. https://ja.wikipedia.org/wiki/クワイン_(プログラミング)

  3. あなたの知らない超絶技巧プログラミングの世界

12
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?