dockerfileのCMDやENTRYPOINTでの変数展開について

前提

dockerfileの CMDENTRYPOINT でコマンドを定義する方法は2通りある

  • exec form(推奨) : JSONの配列で定義する ["echo", "${HOGE}"]
    • 直接コマンドが実行される(変数展開が行われない )
  • shell form : 文字列で定義する "echo ${HOGE}"
    • /bin/sh -c を介してコマンドが実行される(変数展開が行われる )

なので、CMDENTRYPOINT で変数展開したい場合、shell form を使用すればよい

exec formで変数展開を行いたい場合

exeform の中で shell を呼び出せば良い

ENTRYPOINT ["/bin/sh", "-c", "echo ${HOGE}"]

参考

おまけ

以下のように定義すると、HOGE は出力されず、空文字が出力される

ENTRYPOINT ["/bin/sh", "-c", "echo", "${HOGE}"]

これは、${HOGE} が、echo ではなく、/bin/sh の第二引数に渡るため

(第二引数は特に何もしない)

CMDとENTRYPOINT違いおぼえがき

きちんと理解したら全然意味が異なるものだった

ENTRYPOINTとは

docker run 時に実行されるコマンドを定義

apacheを起動させるコンテナ(オフィシャルじゃないけど、説明に最適だった)ENTRYPOINT

ENTRYPOINT ["/usr/sbin/apache2"]

docker run した場合 /usr/sbin/apache2 が実行される

  • ただし、--entrypoint オプションで変更可能

これがあるコンテナは、apacheのコンテナの様に単一の用途を持つという印象

CMDとは

docker run 時のコンテナのデフォルトを定義

この、「デフォルト」は2つの意味がある

  • docker run 時の ENTRYPOINTデフォルトの引数
  • docker run 時に デフォルトで実行されるコマンド

例1. ENTRYPOINT のデフォルトの引数

apache のコンテナCMD

CMD ["-D", "FOREGROUND"]

docker run した場合 ENTRYPOINT と合わせて /usr/sbin/apache2 -D FOREGROUND が実行される

もし、docker run に引数を与えた場合、CMD で定義された部分だけが置き換えられる

例えば、docker run -v を実行した場合、コンテナ上では /usr/sbin/apache2 -v が実行される

例2. デフォルトの振る舞い

alpine linuxのコンテナCMD

CMD ["/bin/sh"]

なので、

  • docker run -it とすれば、シェルに入ることができるし、
  • docker run -it echo "hello" とかすれば、任意のコマンドを実行できる

まとめ

  • ENTRYPOINTdocker run 時に実行されるコマンドを定める
  • CMDdocker run 時のデフォルトを定める。デフォルトとは
    • ENTRYPOINT のデフォルトの引数
    • デフォルトで実行されるコマンド

おまけ

ENTRYPOINTCMD 両方が実行可能な場合、CMD は無視される様子

FROM ubuntu:latest
ENTRYPOINT ["/bin/sh", "-c", "echo 123"]
CMD ["/bin/sh", "-c", "echo aaaa"]

これを docker run すると、123 が出力される

感想

メジャーなコンテナのDockerfileを見たけど、CMD 単一定義のものも多いので、そこまで厳密に定めなくても良いような気もしてきた

herokuのビルドをキャンセルする方法

heroku.yml の設定を間違えたりすると、デプロイ後、以下のエラーが出てタイムアウトするまで待たされて困るので、キャンセル方法を調べた

remote: Compressing source files... done.
remote: Building source:
remote: === Fetching app code
remote:
remote: =!= Unknown error

方法

heroku builds プラグインをインストール後、以下のコマンドを実行

heroku builds:cancel --app ${app_name}

heroku builds プラグインのインストール方法

heroku plugins:install heroku-builds

AWS cli で アカウントに紐づいたロールの切り替えをする方法

課題

セキュリティ上の理由等で、普段管理画面上でロールの切り替えを行っているけれど、それをCLI上でやる場合どうするの?

解決方法

ロール切り替え用のprofileを設定して、それを指定してあげれば良い

~/.aws/config

[default]
output = json
region = ap-northeast-1

[profile adminrole]
source_profile = default
role_arn = arn:aws:iam::***:role/AdminRole
region = ap-northeast-1

profile adminrole が、ロール切り替え後のprofile。 role_arn に切り替え先のロールを設定する

確認

profile default の場合

$ aws sts get-caller-identity  --profile default
{   
    "UserId": "********",
    "Account": "*********",
    "Arn": "arn:aws:iam::********:user/lcl-kobayashi"
}

arn が、IAMユーザであることがわかる

profile adminrole の場合

$ aws sts get-caller-identity  --profile adminrole
{   
    "UserId": "********:botocore-session-********",
    "Account": "********",
    "Arn": "arn:aws:sts::********:assumed-role/AdminRole/botocore-session-********"
}

adminrole のprofile の場合、Arnが、IAMロールに変わっている

上記 profile adminrole--profile なしで使用したい場合

環境変数 AWS_PROFILE を使用する

export AWS_PROFILE=adminrole

参考

Varnish4.1 では最初の `Set-Cookie` しか read/write できない

先に結論

  • Varnish4.1 では最初の Set-Cookie しか read/write できないし、writeすると他の Set-Cookie は消える
  • varnish-modulesのcookieやheaderを使えば、他の Set-Cookie もread/writeできる

サンプルコード

vcl

vcl 4.0;

import std;
import directors;

sub vcl_init {
}

sub vcl_recv {
  return (hash);
}

sub vcl_pipe {
  return (pipe);
}

sub vcl_pass {
}

sub vcl_hash {
  hash_data(req.url);
}

sub vcl_hit {
  return (fetch);
}

sub vcl_miss {
  return (fetch);
}

sub vcl_backend_response {
  return (deliver);
}

sub vcl_deliver {
  if ( resp.http.set-cookie ~ "abc=123" ){
    set resp.http.set-cookie = "zzz=123";
  }

  return (deliver);
}

sub vcl_purge {
}

sub vcl_synth {
  return (deliver);
}


sub vcl_fini {
  return (ok);
}

varnishtest

varnishtest "example"

server s1 {
    rxreq
    expect req.url == "/"
    txresp -hdr "set-cookie: abc=123"  -hdr "set-cookie: def=456"
}
server s1 -start

varnish v1 -vcl+backend {
    include "/etc/varnish/default.vcl";
} -start

client c1 {
    txreq -url "/"
    rxresp
    expect resp.status == 200
    expect resp.http.set-cookie == "zzz=123"
    expect resp.http.set-cookie == "def=456"

} -run

テストの実行結果(一部)

**** c1    1.7 http[ 0] | HTTP/1.1
**** c1    1.7 http[ 1] | 200
**** c1    1.7 http[ 2] | OK
**** c1    1.7 http[ 3] | Content-Length: 0
**** c1    1.7 http[ 4] | Date: Mon, 06 May 2019 11:15:49 GMT
**** c1    1.7 http[ 5] | X-Varnish: 1001
**** c1    1.7 http[ 6] | Age: 0
**** c1    1.7 http[ 7] | Via: 1.1 varnish-v4
**** c1    1.7 http[ 8] | set-cookie: zzz=123
**** c1    1.7 http[ 9] | Accept-Ranges: bytes
**** c1    1.7 http[10] | Connection: keep-alive

vcl_deliver にある set resp.http.set-cookie = "zzz=123"; が実行されると、 set-cookie: def=456" が消えてしまう...!

結論

複数のcookieをread/writeしたい時には、varnish-modules の cookie を使いましょう

参考

labs.gree.jp

labs.gree.jp

Rubyでインクリメンタルする Quine AA を書いた

デモ

実行するごとにAAの白抜きの数字が増えていきます

コード

コードジェネレータ

前回解説したように、Quine AA は、AAを作るコード自身をAAに変換するジェネレータを作って作成します

blog.kasei-san.com

解説

  • k=0; がどの数字を表示するか決定します
  • base64の配列は0〜9までの数字のAAのデータです
  • その後は、数字のデータを元にソースコードをAAに展開するコードです
    • 途中の 0.upto(70){o<<e[j+=1]};o<<10;0.upto(25){o<<e[j+=1]}; はマージンです
    • もっと短くできるはず...
  • ポイントは 29行目の o[12] = ((o[12].to_i+1)%10).to_s; で、出力するコードの k=0; の数字をインクリメントしたものに変換しています
    • これにより、次に表示されるAAがインクリメントされた数字になります

感想

  • Quineで状態を変更するの、どうしたら良いのかと最初悩んだが、outputするコードを直接書き換えれば良いことに気がつけてよかった
  • Rubyist界隈ではQuine程度ではまだまだのようなので、次は別のキモいコードにチャレンジしてみます!

Rubyで書く! Quine AA!

こちらの記事を元に自分で作ってみた時の記録です

mickey24.hatenablog.com

ソースコード

     eval$s=%w'     
   b="BAhsK2Lgf4    
 D/D/7/8/9///j/B/9  
/8P8H/3/w/wf/f/7/7/ 
///+//P/   j/Af5/AOA
PAPwAAM     CHH3z4wY
cffPjBh     x98+MGHH
/z/wf8/     /P+D/z/g
nwMAAAA     PAPgBAA8
AAADAPw     D8A4A/AP
gDgB8A+  AGAHwD4AeA/
AP4D4D8A /gMAAOD8A/7
/4P8f/v/D/z/88MMPP/z
 www8//PDDDz/88AMA  
   APD/wP8//v//D/   
     7/////f///8x8A 
             /wPg/z/
              8/wP/P
                    
  wA=";    n=Mars   
  hal.l    oad(b.   
  unpac    k("m")   
  [0]);    x=20;y   
  =74;e    ="eval   
  $s=%w    "<<39<   
  <($s*    3);o="   
  ";j=-1;0.upto(x   
  *y){|i|o<<((n[i]  
  ==1)?e[j+=1]:32)  
   ;o<<((i%x==x-1)  
     ?10:"")}  ;o[  
                    
        -10,        
       6]=""<       
        <39<        
                    
      <".join"      
      ;puts(o)      
       ;#b="BA      
       hsK2Lgf      
       4D/D/7       
       /8/9//       
       /j/B/9       
       /8P8H/       
     3/w/wf/f/      
     7/7////+/      
     /P/j/Af5/      
     AOAPAPwAA      
                    
 MCH  H3z4wYcf      
 fPjBhx98+MGHH/z    
 /wf8//P+D/z/gnwM   
 AAAAPAPgBAA8AAADA  
  PwD8A4A/APgDgB8A  
  +AGAHw    D4AeA/  
  AP4D4D    8A/gMA  
  AOD8A/    7/4P8f  
  /v/D/z    /88MMP  
  P/zwww    8//PDD  
  Dz/88A    MAAPD/  
  wP8//v    //D/7/  
                    
    ////f///8x8A    
  /wPg/z/8/wP/PwA=  
 ";n=Marshal.load(b.
unpack("     m")[0])
;x=20;y=74;e="eval$s
=%w"<<39<<($s*3);o= 
"";j=-1;0.upto(x*y  
){|i|o<<(           
(n[i]==1)?          
 e[j+=1]:32);o<<((  
  i%x==x-1)?10:"")  
    };o[-10,'.join  

キモいですね!

作り方

以下の手順に沿ってコードを書いていく

  1. QuineにしたいAAを用意
  2. AAをBase64文字列に変換
  3. 「Base64文字列からAAを表示する」ジェネレーターを作成
  4. 「AAジェネレータ自身のコードをAAにする」ジェネレーターを作成
  5. 「AAジェネレーターをAAのQuineにする」ジェネレータを作成

AAのコードを実行可能にして、さらにQuine化

1. QuineにしたいAAを用意

このサイトで文字列をAAにできるので、これを元に作成。おすすめのフォントはdoh

https://www.askapache.com/online-tools/figlet-ascii/

aa=<<EOS
00000111111111100000
00011111111111110000
01111111111111111100
11111111111111111110
11111111000111111111
11111110000011111111
11111110000011111111
11111110000011111111
11111110000011111111
11111110000011111111
11111110011111111111
11111111011111111111
11111111111111111111
01111111111111111100
00011111111111111000
00000111111111111110
00000000000001111111
00000000000000111111
00000000000000000000
00111110000111111000
00111110000111111000
00111110000111111000
00111110000111111000
00111110000111111000
00111110000111111000
00111110000111111000
00111111111111111000
00111111111111111100
00111111111111111100
00011111111111111100
00000111111110011100
00000000000000000000
00000000111100000000
00000001111110000000
00000000111100000000
00000000000000000000
00000011111111000000
00000011111111000000
00000001111111000000
00000001111111000000
00000001111110000000
00000001111110000000
00000001111110000000
00000001111110000000
00000111111111000000
00000111111111000000
00000111111111000000
00000111111111000000
00000000000000000000
01110011111111000000
01111111111111110000
01111111111111111000
01111111111111111100
00111111111111111100
00111111000011111100
00111111000011111100
00111111000011111100
00111111000011111100
00111111000011111100
00111111000011111100
00111111000011111100
00000000000000000000
00001111111111110000
00111111111111111100
01111111111111111111
11111111000001111111
11111111111111111111
11111111111111111110
11111111111111111100
11111111100000000000
11111111110000000000
01111111111111111100
00111111111111111100
00001111111111111100
EOS

解説

  • このAAの1 の部分に後ほどソースコードが入る
  • ソースコードの文字数は当然ながら、AAで表現できる文字数よりも少ない必要がある
  • ソースコードにAAのデータも入るため、base64に変換して、格納可能にする

2. AAをBase64文字列に変換

普通に [Marshal.dump(bits)].pack("m") を使って変換

bits = aa.gsub("\n", "").reverse.to_i(2)
bin = [Marshal.dump(bits)].pack("m").gsub("\n", "")
puts bin #=>BAhsK2Lgf4D/D/7/8/9///j/B/9/8P8H/3/w/wf/f/7/7////+//P/j/Af5/AOAPAPwAAMCHH3z4wYcffPjBhx98+MGHH/z/wf8//P+D/z/gnwMAAAAPAPgBAA8AAADAPwD8A4A/APgDgB8A+AGAHwD4AeA/AP4D4D8A/gMAAOD8A/7/4P8f/v/D/z/88MMPP/zwww8//PDDDz/88AMAAPD/wP8//v//D/7/////f///8x8A/wPg/z/8/wP/PwA=

作成したBase64文字列は次のコードで使用するのでコピーする

3. 「Base64文字列からAAを表示する」ジェネレーターを作成

ジェネレータの概要

  1. Base64文字列にしたデータをデコード
  2. デコードしたBase64文字列を元に、0ならば空白。1ならば '1' を出力
  3. もともとのAAのサイズを元に、一定数出力したら改行

コード

# ↓AAをBase64文字列にしたデータ
b="BAhsK2Lgf4D/D/7/8/9///j/B/9/8P8H/3/w/wf/f/7/7////+//P/j/Af5/AOAPAPwAAMCHH3z4wYcffPjBhx98+MGHH/z/wf8//P+D/z/gnwMAAAAPAPgBAA8AAADAPwD8A4A/APgDgB8A+AGAHwD4AeA/AP4D4D8A/gMAAOD8A/7/4P8f/v/D/z/88MMPP/zwww8//PDDDz/88AMAAPD/wP8//v//D/7/////f///8x8A/wPg/z/8/wP/PwA="

# Base64文字列をデコード
n=Marshal.load(b.unpack("m")[0])

# AAのサイズ
x=20;y=74

# 出力結果が格納される変数
o=""

# デコードしたBase64文字列を展開
# 0ならば空白。1ならば '1' を出力
# 後でQuine化するときに改行や空白は使えないので数値で表現
0.upto(x*y){|i|
  o<<((n[i]==1)?'1':32) # 32 は空白
  o<<((i%x==x-1)?10:"") # 10 は改行
}
puts(o)

実行結果

     1111111111     
   1111111111111    
 11111111111111111  
1111111111111111111 
11111111   111111111
1111111     11111111
1111111     11111111
1111111     11111111
1111111     11111111
1111111     11111111
1111111  11111111111
11111111 11111111111
11111111111111111111
 11111111111111111  
   11111111111111   
     11111111111111 
             1111111
              111111
                    
  11111    111111   
  11111    111111   
  11111    111111   
  11111    111111   
  11111    111111   
  11111    111111   
  11111    111111   
  111111111111111   
  1111111111111111  
  1111111111111111  
   111111111111111  
     11111111  111  
                    
        1111        
       111111       
        1111        
                    
      11111111      
      11111111      
       1111111      
       1111111      
       111111       
       111111       
       111111       
       111111       
     111111111      
     111111111      
     111111111      
     111111111      
                    
 111  11111111      
 111111111111111    
 1111111111111111   
 11111111111111111  
  1111111111111111  
  111111    111111  
  111111    111111  
  111111    111111  
  111111    111111  
  111111    111111  
  111111    111111  
  111111    111111  
                    
    111111111111    
  1111111111111111  
 1111111111111111111
11111111     1111111
11111111111111111111
1111111111111111111 
111111111111111111  
111111111           
1111111111          
 11111111111111111  
  1111111111111111  
    11111111111111  
 

このAAジェネレータを元に、「AAジェネレータ自身のコードをAAにする」ジェネレーターを作成

ややこしくなってまいりました

4. 「AAジェネレータ自身のコードをAAにする」ジェネレーターを作成

概要

  • eval$s=%w'〜'.join を使って、$sをAAの材料にするジェネレータを作成する
  • の中にはAAジェネレータが入る

ソースコード

eval$s=%w'b="BAhsK2Lgf4D/D/7/8/9///j/B/9/8P8H/3/w/wf/f/7/7////+//P/j/Af5/AOAPAPwAAMCHH3z4wYcffPjBhx98+MGHH/z/wf8//P+D/z/gnwMAAAAPAPgBAA8AAADAPwD8A4A/APgDgB8A+AGAHwD4AeA/AP4D4D8A/gMAAOD8A/7/4P8f/v/D/z/88MMPP/zwww8//PDDDz/88AMAAPD/wP8//v//D/7/////f///8x8A/wPg/z/8/wP/PwA=";
n=Marshal.load(b.unpack("m")[0]);
x=20;
y=74;
e=$s*3;
o="";
j=-1;
0.upto(x*y){|i|o<<((n[i]==1)?e[j+=1]:32);o<<((i%x==x-1)?10:"")};
puts(o);
#
'.join

解説

  • eval$s=%w'〜'.join を使って、%$(すなわちジェネレータのコード) をAAにしている
    • %w'〜'.join することで、AAに余分な改行や空白が入らないようにしている
    • 結果としてコードには、改行や空白や ' をいれることはできない
  • e=$s*3; で、AA化するソースコードが、AAの文字数より多くなるように水増ししている
    • 10行目の # により、2回目以降出力されるコードはコメント扱いになる
  • 0.upto(x*y){|i|o<<((n[i]==1)?e[j+=1]:32);o<<((i%x==x-1)?10:"")}; にて、e に格納されたソースコードをAAに流し込んでいる

実行結果

     b="BAhsK2L     
   gf4D/D/7/8/9/    
 //j/B/9/8P8H/3/w/  
wf/f/7/7////+//P/j/ 
Af5/AOAP   APwAAMCHH
3z4wYcf     fPjBhx98
+MGHH/z     /wf8//P+
D/z/gnw     MAAAAPAP
gBAA8AA     ADAPwD8A
4A/APgD     gB8A+AGA
HwD4AeA  /AP4D4D8A/g
MAAOD8A/ 7/4P8f/v/D/
z/88MMPP/zwww8//PDDD
 z/88AMAAPD/wP8//v  
   //D/7/////f///   
     8x8A/wPg/z/8/w 
             P/PwA="
              ;n=Mar
                    
  shal.    load(b   
  .unpa    ck("m"   
  )[0])    ;x=20;   
  y=74;    e=$s*3   
  ;o=""    ;j=-1;   
  0.upt    o(x*y)   
  {|i|o    <<((n[   
  i]==1)?e[j+=1]:   
  32);o<<((i%x==x-  
  1)?10:"")};puts(  
   o)b="BAhsK2Lgf4  
     D/D/7/8/  9//  
                    
        /j/B        
       /9/8P8       
        H/3/        
                    
      w/wf/f/7      
      /7////+/      
       /P/j/Af      
       5/AOAPA      
       PwAAMC       
       HH3z4w       
       YcffPj       
       Bhx98+       
     MGHH/z/wf      
     8//P+D/z/      
     gnwMAAAAP      
     APgBAA8AA      
                    
 ADA  PwD8A4A/      
 APgDgB8A+AGAHwD    
 4AeA/AP4D4D8A/gM   
 AAOD8A/7/4P8f/v/D  
  /z/88MMPP/zwww8/  
  /PDDDz    /88AMA  
  APD/wP    8//v//  
  D/7///    //f///  
  8x8A/w    Pg/z/8  
  /wP/Pw    A=";n=  
  Marsha    l.load  
  (b.unp    ack("m  
                    
    ")[0]);x=20;    
  y=74;e=$s*3;o=""  
 ;j=-1;0.upto(x*y){|
i|o<<((n     [i]==1)
?e[j+=1]:32);o<<((i%
x==x-1)?10:"")};put 
s(o)b="BAhsK2Lgf4D  
/D/7/8/9/           
//j/B/9/8P          
 8H/3/w/wf/f/7/7//  
  //+//P/j/Af5/AOA  
    PAPwAAMCHH3z4w  

ここからジェネレータさらに加工して、上記AAを実行できるようにする

5. 「AAジェネレーターをAAのQuineにする」ジェネレータを作成

  • Syntax Error 回避のために、上記のコードを文字列として結合して、eval する処理を追加する
  • さらに、それをQuine化する

ベースとなるQuine

eval$s=%w'o="eval$s=%w"<<39<<$s<<39<<".join";puts(o)'.join
  • 39 は、'%w'〜'の中で'を使用するために数値を使用

このQuineは、o="...の前に任意のコードを埋めることができる

eval$s=%w'a="aaa";o="eval$s=%w"<<39<<$s<<39<<".join";puts(o)'.join

それを利用して以下のように、AAを作成するコードをQuineに埋め込む

ソースコード

eval$s=%w'b="BAhsK2Lgf4D/D/7/8/9///j/B/9/8P8H/3/w/wf/f/7/7////+//P/j/Af5/AOAPAPwAAMCHH3z4wYcffPjBhx98+MGHH/z/wf8//P+D/z/gnwMAAAAPAPgBAA8AAADAPwD8A4A/APgDgB8A+AGAHwD4AeA/AP4D4D8A/gMAAOD8A/7/4P8f/v/D/z/88MMPP/zwww8//PDDDz/88AMAAPD/wP8//v//D/7/////f///8x8A/wPg/z/8/wP/PwA=";
n=Marshal.load(b.unpack("m")[0]);
x=20;
y=74;
e="eval$s=%w"<<39<<($s*3);
o="";
j=-1;
0.upto(x*y){|i|o<<((n[i]==1)?e[j+=1]:32);o<<((i%x==x-1)?10:"")};
o[-10,6]=""<<39<<".join";
puts(o);
#
'.join

解説

  • e="eval$s=%w"<<39<<($s*3); が、Quineの先頭部分
  • o[-10,6]=""<<39<<".join"; で、AAで出力されるコードの末尾を '.join に置き換えて、Quineの末尾にしている

実行結果

     eval$s=%w'     
   b="BAhsK2Lgf4    
 D/D/7/8/9///j/B/9  
/8P8H/3/w/wf/f/7/7/ 
///+//P/   j/Af5/AOA
PAPwAAM     CHH3z4wY
cffPjBh     x98+MGHH
/z/wf8/     /P+D/z/g
nwMAAAA     PAPgBAA8
AAADAPw     D8A4A/AP
gDgB8A+  AGAHwD4AeA/
AP4D4D8A /gMAAOD8A/7
/4P8f/v/D/z/88MMPP/z
 www8//PDDDz/88AMA  
   APD/wP8//v//D/   
     7/////f///8x8A 
             /wPg/z/
              8/wP/P
                    
  wA=";    n=Mars   
  hal.l    oad(b.   
  unpac    k("m")   
  [0]);    x=20;y   
  =74;e    ="eval   
  $s=%w    "<<39<   
  <($s*    3);o="   
  ";j=-1;0.upto(x   
  *y){|i|o<<((n[i]  
  ==1)?e[j+=1]:32)  
   ;o<<((i%x==x-1)  
     ?10:"")}  ;o[  
                    
        -10,        
       6]=""<       
        <39<        
                    
      <".join"      
      ;puts(o)      
       ;#b="BA      
       hsK2Lgf      
       4D/D/7       
       /8/9//       
       /j/B/9       
       /8P8H/       
     3/w/wf/f/      
     7/7////+/      
     /P/j/Af5/      
     AOAPAPwAA      
                    
 MCH  H3z4wYcf      
 fPjBhx98+MGHH/z    
 /wf8//P+D/z/gnwM   
 AAAAPAPgBAA8AAADA  
  PwD8A4A/APgDgB8A  
  +AGAHw    D4AeA/  
  AP4D4D    8A/gMA  
  AOD8A/    7/4P8f  
  /v/D/z    /88MMP  
  P/zwww    8//PDD  
  Dz/88A    MAAPD/  
  wP8//v    //D/7/  
                    
    ////f///8x8A    
  /wPg/z/8/wP/PwA=  
 ";n=Marshal.load(b.
unpack("     m")[0])
;x=20;y=74;e="eval$s
=%w"<<39<<($s*3);o= 
"";j=-1;0.upto(x*y  
){|i|o<<(           
(n[i]==1)?          
 e[j+=1]:32);o<<((  
  i%x==x-1)?10:"")  
    };o[-10,'.join  
 

これで完成!!

ポイント

「AAのデータとアスキーアートジェネレーターをAAのQuineにする」ジェネレータを作って、AAのQuineを作成している

  • AAをBase64にしてコードに埋め込んでいる
  • AAの文字数とソースコードの文字数の不一致を何とかするために、コードの末尾をコメントにしてコードを3倍にしたものをAAに使用している
    • そのため、末尾の5文字はコメントのハズなので、'.join で置換しても問題がない
  • 基本となる Quine は eval$s=%w'puts"eval$s=%w"<<39<<$s<<39<<".join"'.join
    • eval$s='〜'.join というコードのために、' と空白文字が使用できない

感想

evalを使ったQuineは脳が裏返る感じがする