11
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

動く!電光掲示板シェル芸

 ⚠注意⚠: 点滅GIF画像が出てくるので、明るい部屋で画面から十分画面から離れてご覧ください。

 この記事はシェル芸 Advent Calendar 2019klis Advent Calender 2019のクロスポストになりました。

まえがき

 今回の記事では、動くタイプの電光掲示板シェル芸について解説します。
 具体的には文字を電光掲示板ライクに変換して、文字を画像にするtextimgコマンドのオプションを色々使って動かします。
 基本的(?)な電光掲示板シェル芸については@yami_buta氏が本AdventCalendarで過去3回ほど解説1をされているので、先にそちらの方に目を通されることをおすすめします。

おさらい 〜電光掲示板シェル芸とは〜

 「LED表示みたいな画像を生成する」2シェル芸。
▼参考画像3
kohno.png
 今回はこれを動かします。
 こんなことができます。

▼点滅とか…4
gameover.gif
▼縦スクロール5
tenmetsu.gif
▼掲示板っぽい横スクロール6
kohnosc.gif
▼明暗7
internet.gif

やってみる

STEP0: 電光掲示板文字っぽく出力

 今回は文字Qiitaを動かしたいと思います。
 まずdenkoh関数を定義します。(定義ツイート)
 この関数は文字を任意の文字でAAのように変換したものを返し、簡単に電光掲示板風に変換します。
 最近だと@jiro4989氏がコマンドにしたもの8もあるのでそちらを使っても良いです。

denkoh
# denkoh <電光掲示板風にしたい文> <背景文字> <前景文字> <粗さ>
# dtimgだと:
# dtimg <電光掲示板風にしたい文> -b <背景文字> -g <前景文字> -F <粗さ>
# .bash_aliases`などに追記しておくと便利。
denkoh(){
  echo -e "$1"|textimg -F$4|convert - txt:-|awk -F'[, ]' \
  'NR>2{printf($1==0)?"\n":($4==0)?a:b}' a="$2" b="$3";echo
}

これを使うと、

Terminal
$ denkoh "Qiita" " " "■ " 20
                                                 
                                                 
             ■■■       ■■■                       
 ■■■■■■      ■■■       ■■■                       
■■■■■■■■     ■■■       ■■■      ■■               
■■■  ■■■     ■■■       ■■■      ■■               
■■   ■■■                        ■■               
■■    ■■■  ■■■■■     ■■■■■      ■■       ■■■■■■  
■■    ■■■  ■■■■■     ■■■■■    ■■■■■■■■  ■■■■■■■■ 
■■    ■■■    ■■■       ■■■    ■■■■■■■■  ■■■  ■■■ 
■■    ■■■    ■■■       ■■■      ■■           ■■■ 
■■    ■■■    ■■■       ■■■      ■■       ■■■■■■■ 
■■    ■■■    ■■■       ■■■      ■■      ■■■■■■■■ 
■■    ■■■    ■■■       ■■■      ■■      ■■■  ■■■ 
■■    ■■     ■■■       ■■■      ■■      ■■   ■■■ 
■■■  ■■■     ■■■       ■■■      ■■      ■■   ■■■ 
■■■■■■■■  ■■■■■■■■  ■■■■■■■■    ■■■■■■  ■■■■■■■■ 
 ■■■■■■   ■■■■■■■■  ■■■■■■■■    ■■■■■■  ■■■■■■■■ 
  ■■■■■■                         ■■■■     ■■■    
     ■■■■                                        
      ■■■                                        
                                                 

 という具合に文字が出力できます。
 この出力をtextimgに標準入力で渡すと画像になります。

Terminal
$ denkoh "Qiita" " " "■ " 20|textimg -o qiita.png

qiita.png

STEP1: 点滅

 点滅させてみましょう。
 同じ行数の空行を出力し、行数指定(textimg-lオプション)で[文字部分n行]->[空行部分n行]となるように調整してコマを切り替えGIF化します。
 ここでは任意の空行を作るため、eval 'echo;'{<任意数個カンマ>}とします。eval 'echo;'{,,,,}{,,,}とすると5*4=20回echoが評価されて20行分空行が作れます。
 ちなみに数で明示的に空行を作るならseq <任意行数>|tr -d 0-9でも同じことができます。
 また複数コマンドの標準出力をまとめてパイプ先コマンドに渡す時は(cmd1;cmd2)|cmd3とします。便利〜

Terminal
#全体の行数の半分を-lに指定
$ (denkoh "Qiita" " " "■" 20;eval 'echo;'{,,,,,}{,,} )|wc -l
42
$ (denkoh "Qiita" " " "■" 20;eval 'echo;'{,,,,}{,,,})|textimg -al21 -o tenmetsu1.gif

tenmetsu1.gif
 目に痛いときは-dオプションで速度を落としましょう。(デフォルトは20ms)

Terminal
$ (denkoh "Qiita" " " "■" 20;eval 'echo;'{,,,,}{,,,})|textimg -al21 -d50 -o tenmetsu2.gif

tenmetsu2.gif
 いい感じです。
 色コードの指定9で色をつけることもできますが、色のついた絵文字を指定することで簡単に色をつけられます。また後述のlolcatという出力を虹色にするコマンドとも親和性が良いのでそれを使用しても楽しいです。

Terminal
$ (denkoh "Qiita" " " "🍀" 20;eval 'echo;'{,,,,}{,,,} )|textimg -al21 -o tenmetsu3.gif -d50

tenmetsu3.gif

STEP2: 縦スクロール

 次は縦に動かします。
 -Sオプションは、デフォルトで1行ずつ移動します。

Terminal
$ (eval 'echo;'{,,,,}{,,,};denkoh Qiita " " "🍀" 20)|textimg -aSl21 -o tatesc1.gif

tatesc1.gif
 -Wオプションで任意の行数移動。

Terminal
$ (eval 'echo;'{,,,,}{,,,};denkoh Qiita " " "🍀" 20)|textimg -aSl21 -W3 -o tatesc2.gif

tatesc2.gif
 そして-Eオプションで、EOSでまた行頭にループさせます。

Terminal
$ (eval 'echo;'{,,,,}{,,,};denkoh Qiita " " "🍀" 20)|textimg -aSEl21 -W3 -o tatesc3.gif

tatesc3.gif
 空行を減らして-dでコマの切り替え速度をアップ!

Terminal
$ (eval 'echo;'{,,,,}{,,,};denkoh Qiita " " "🍀" 20)|textimg -aSEl21 -W3 -o tatesc4.gif

tatesc4.gif

STEP3: 横スクロール

 電光掲示板でよくある横方向のスクロール。これを実現するにはsedを使ってdenkoh出力を任意文字分を行末から行頭へ回転移動させることを繰り返します。

Terminal
#一旦出力を変数に格納
$ s="$(denkoh Qiita ' ' '🍀' 20)"
$ for i in $(seq 10) #ずらす回数分ループ, 一度に5文字ずつずらす
    do echo "$s"|sed -E 's/(.{'$[i*5]'})(.*)/\2\1/' 
  done|textimg -al22 -o yoko.gif #もともとの行数+2

yoko.gif
 いい感じです。
 この処理は、denkohmoveという関数に定義しておくと便利です。(定義ツイート)10

denkohmove
# denkohmove <電光掲示板風にしたい文> <背景文字> <前景文字> <粗さ> \
#            <移動する幅> <ずらす回数(=コマ数)> <出力時の解像度> <コマの切り替え速度>
# ex: denkohmove ' 🚙 🚗 🚙 🚗' ' ' '⚪' 10 \
#                8 7 5 10 
denkohmove(){
  local text back front line width flame resolute speed
  text="$1" back="$2" front="$3" line="$4"
  width="$5" flame="$6" resolute="$7" speed="$8"
  s="$(denkoh ${text} ${back} ${front} ${line})"
  for i in $(seq ${flame})
    do echo "$s"|sed -E 's/(.{'$[i*width]'})(.*)/\2\1/'
  done|textimg -asl$[line+1] -F${resolute} -d${speed}
}

STEP4: 明暗

 複数文字でのdenkoh出力を組み合わせ繰り返すことで明暗の反復を表現できます。
 「明」は中身のある文字(例:●,▲)、「暗」はない文字(例:○,△)で表現します。

Terminal
$ s="`denkoh Qiita ' ' ■ 20`"
$ for i in ● ○
    do echo -e "$s"|sed "s/■/$i/g"
  done|textimg -al22 -o meimetsu1.gif

meimetsu1.gif

 明暗の文字を連続して切り替えるとメリハリ(?)が付きます。

Terminal
$ s="`denkoh Qiita ' ' ■ 20`"
$ for i in ● ○ ▲ △ ● ○ ▲ △ #明暗の文字を交互に配置する
    do echo -e "$s"|sed "s/■/$i/g"
  done|textimg -al22 -o meimetsu2.gif

meimetsu2.gif

 また出力時の行数を2倍にすると明暗を並べて表現できます。

Terminal
$ s="`denkoh Qiita ' ' ■ 20`"
$ for i in ● ○ ▲ △ ● ○ ▲ △
    do echo -e "$s"|sed "s/■/$i/g"
  done|textimg -al44 -o meimetsu3.gif

meimetsu3.gif

 コマンドラインを虹色にするのでおなじみlolcatコマンド11で着色してみます。-fオプションは、パイプで渡しても色コードが失われないようにするオプションです。また-pオプションは虹の次の色へ変わるまでの間隔、-Fオプションは色の機微が遷移する間隔を指定します。

Terminal
$ s="`denkoh Qiita ' ' ■ 20`"
$ for i in ● ○ ▲ △ ● ○ ▲ △
    do echo -e "$s"|sed "s/■/$i/g"
  done|lolcat -f|textimg -al22 -o meimetsu4.gif

meimetsu4.gif

 いいですね。

 ちなみに、STEP3とこの手法を組み合わせて各ループごとの偶数番目と奇数番目で前景文字を異なる文字で置換することで、冒頭例のようにチカチカした感じを表現できます。ここでは「明」を白混じりの🍺、「暗」を黄いろい🌟に指定しています。

Terminal
#ずらす作業の関数化
$ f(){ echo "$d"|sed -E 's/(.{'$[i*9]'})(.*)/\2\1/';}
$ d="$(denkoh Qiita ' ' '🍺' 20)"
$ for i in {1..5}
    do [[ $((i%2)) = 0 ]]&&f || { f|sed 's_🍺_🌟_g';} #奇数番目ループなら置換
  done|textimg -al22 -o chika.gif

chika.gif 
 派手〜!

STEP5: シェル芸botに投稿!

 シェル芸botは皆さんご存知、フォロワーの#シェル芸付きのツイートをコマンドとして評価し結果をツイートで返すbotです。
 シェル芸botには、コンテナ内の/images/直下に画像ファイルをコマンド内で配置することでその画像を投稿できる機能があります。(静/動画ともに最大4枚まで)
 textimgにはこのために画像の出力先を/images/o.[png|gif]にできる-sオプションが用意されています。上記の例の-oオプション部分を削除して代わりに-sを使いましょう。
 また、投稿が1ツイートに収まりきらない場合や、他人がツイートで定義した関数・変数を使用したいなら、コメント付き引用RTでそのツイートをRTすることで実現できます。(参考: denkohの定義ツイートとそれを引用したツイート)

備考: 投稿できない時 && テスト環境の紹介

 しかし、今回取り上げたGIF画像を実行しようとしてもうまく結果がbotに返されないこともあります。理由としては、

  • シェル芸botの実行TLE(制約20s)
    • timeout 20 <ツイート>で実行している(?)
  • 画像サイズ超過
    • 静止画 : 5MB
    • GIF動画 : 15MB(スマホなら5MB)

 が挙げられ、殆どは前者が原因です。計算量を落とすには、

  • textimgでの出力時に-Fオプションでデフォルトの20より少ない数を指定して解像度を落とす(denkohなら最後の数字)
  • denkohで出力する行数を減らす
  • STEP3ならずらす幅を大きくしループを少なくしてコマを減らす

 などの方法があります。投稿できる計算量にしてからツイートを投稿しましょう。
 シェル芸bot作者の@theoldmoon0602氏によってbotの最新dockerイメージが配布12されているのでそれを使用してもいいですが、画像を逐一確認しながら20秒制限を調整するには、以下のWeb実行環境(各種ネタコマンド完備)でテストするのが無難です。13

実行されるまで#シェル芸ツイートを連投しTLを汚しながらコマンドを調整する方法もありますが...

結論

textimg便利すぎ!!!!!16

最後に

 いかがでしたか?
 このようにコマンドライン上だけで多種多様なGIF画像ができてしまいます。
 またこの他にも今回紹介した例を応用して様々な画像が作れます。

▼例: 斜めスクロール
kohnosc.gif

▼例: 複数文字を順に17
merry.gif

 お試しあれ。

 余談ですが、今回紹介したtextimgやdtimgはシェル上に画像を投稿するために作られたコマンドです。他にもシェル芸bot上には様々なシェル芸愛好家(通称:シェル芸人)の方々が、主にシェル芸botで使うことを目的として作成したクソ便利なコマンド群が導入されています。18
 是非本記事で使用したコマンドや他の楽しいコマンド群を使って#シェル芸でシェル芸botに投稿してみましょう!
 それでは皆様、良いシェル芸ライフを!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
11
Help us understand the problem. What are the problem?