シェル芸bot環境につぶやいてきたジョークシェル芸をひたすら書き連ねる
今までシェル芸botで実行してきたシェルのうち ボクが気に入ったものや、メモしておきたいものをひたすらここに残します。
ついでにどういうことをやっているのかといった解説もします。
- LISP風の条件分岐を行う関数
- 喘ぎ声生成
- 「あなたは赤い部屋が好きですか」1文字ずつ増やして出力
- うんこで囲う
- 相関図
- 難読化うんこ
- あみだ線生成
- 虹色のうんこ
- 色のついたテキストを画像に変換する
- 殺意のうんこ
- きもいオジサンのLINEメッセージ
- きもいオジサンのメッセージをうんこに埋め込む
LISP風の条件分岐を行う関数
[if
という関数を定義して]
が見つかるまでをシェルのコマンドとみなす。
結果的に内部でif
してるんですけれどね。一応true、falseで実行されるコマンドが変化する。
[if(){ f=2 a=() b=() c=() i=0 for o in "$@";do case "$o" in [)f=1;; ])f=2;let i++;; *) if [ $f = 1 ];then case $i in 0)a+=($o);; 1)b+=($o);; 2)c+=($o);; esac fi ;; esac done if $("${a[@]}");then eval ${b[@]} else eval ${c[@]} fi } [if [ true ] [ echo 1 ] [ echo 2 ] ] #シェル芸
喘ぎ声生成
いわゆるみさくらなんこつ的な喘ぎ声っぽいなにかを生成するシェル芸。
下品すぎる。
p(){ paste -d "" - - - - - - } h(){ head -n 99 } s(){ shuf -re $@ } (s あ ぁ ん ♥ \"|p|grep -vE '^("|♥)'|h; s ら め へ ぇ \"|p|grep -vEe '""|らら|めめ'|grep -E "^ら"|h; s ん ほ お ぉ \"|p|grep -E "^ん"|h; s い く ぅ \"|p|grep -E '^い'|grep -v '""'|h;)|shuf
らへめ"ら" い"くぅ"く ん"ぉんぉお ぁぁぁあ♥ん らぇへめ"ぇ い"いくくく らへへ"めら ん""ん"ぁ んおお"んお いくいぅ"く らへ"へめへ らぇぇめぇぇ
「あなたは赤い部屋が好きですか」1文字ずつ増やして出力
かなり昔に作成されたホラーフラッシュの赤い部屋の最後の方で表示される 少しずつ文字が見えていくというのを再現したシェル(もとのフラッシュだと文字と文字の切れ目から出現していたが)。
本家の赤い部屋フラッシュはもう見れない。 公開が停止されている、という意味ではなく、 ブラウザがフラッシュプラグインをサポートしていないため。 Youtubeで我慢する。
これを当時見たときは本気で怖がっていたような記憶。
echo あなたは赤い部屋が好きですか? | grep -o . | awk '{s=s""$1; print s}'
あ あな あなた あなたは あなたは赤 あなたは赤い あなたは赤い部 あなたは赤い部屋 あなたは赤い部屋が あなたは赤い部屋が好 あなたは赤い部屋が好き あなたは赤い部屋が好きで あなたは赤い部屋が好きです あなたは赤い部屋が好きですか あなたは赤い部屋が好きですか?
技術的な話をすると、grep -o .
は文字列を1列にする。
-o
オプションはマッチした文字だけを出力する。
.
は正規表現での「任意の何らかの文字」にマッチする。
よって、あらゆる文字が1つマッチしたら出力し、次のマッチを待つようになる。
以下の例を示す。
[0-9]
は数値にのみにマッチする正規表現。
結果も数値のみ出力されている。
% echo あ12い34う56え78お | grep -Eo "[0-9]" 1 2 3 4 5 6 7 8
対して.
ではすべての文字が1文字ずつ出力されている。
% echo あ12い34う56え78お | grep -o "." あ 1 2 い 3 4 う 5 6 え 7 8 お
最後はawkで1文字ずつ連結して出力する。
awkでは文字列の連結は+
とかは不要。
文字列や変数をカンマで区切ったらスペース区切り出力され、
カンマをつけずに文字列や変数を続けてprintすると
文字列が連結される。
awk '{s=s""$1; print s}'
なので、こう書いても同じ結果になる。
awk '{s=s $1; print s}'
うんこで囲う
シェル芸bot環境に存在するいくつかのコマンドと組み合わせている。 手前味噌だけれど、alignという自作のコマンドも使用している。
( echo; ( unko.tower -s うんこ神 5 \ | align left -p " " \ | owari kan -gi; echo ) \ | align center -p1 -n 60 \ | sed -r 's/111/うんこ/g;s/11/うん/g;s/1/う/g';echo ) \ | align center -p2 -n 72 \ | sed 's/222/ウンコ/g;s/22/ウン/g;s/2/ウ/g' \ | sed 'y/NHK/うんこ/' \ | imgout #シェル芸
unko.tower
はsuper_unkoリポジトリのコマンドの1つ。
-s
オプションではうんこの中にテキストを埋め込める。
align
はテキストの位置を揃える。
owari
はギコネコが看板を背負っているAAを出力する。
-gi
では標準入力を受け取って、看板内に中央寄せして埋め込んで出力する。
$ unko.tower -s うんこ | align left | owari kan -gi | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄| | 人 | | (うんこ) | | (うんこうん) | | (こうんこうんこ) | | 制作・著作 | |  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ | | N H K | |____________| ∧∧ || ( ゚д゚)|| / づΦ
一番深い階層でギコネコを出力するが、それ全体をサブシェルでラッピングし、 サブシェルをパイプしてalignに渡すことで結果全体を一揃えしている。
同じようなことを繰り返し、空白文字をうんこに置換。
sed
のyコマンドでは1文字ずつ対応するように置換する。
相関図
これは2ツイート連携で実現している。280文字以内には収まった。
やっていることは結局前述のうんこで囲むのと同じことを組み合わせているだけ。
#!/bin/bash ( echo echo 'うんこシェル芸人とショル力の相関'|align center -n 70 -p ' ' ( echo 'y (シェル芸力)' ( seq 10|while read i;do printf "%${i}s\n" '/';done|tac|sed 's/ / /g' | align left -p ' ' | sed -r 's/^/|/g' echo ) | align left -p '~~' \ | sed '11s/$/ x (うんこと呟く頻度)/g' ) \ | align left -n 40 -p ' ' \ | align center -p ' ' -n 70 echo '※1 サンプリング数:255名'|align right -n 70 -p ' ' echo ) | align center -p '-' -n 80
以下抜粋。
seq〜printf
で空白埋めされた線を描画している。左上から右下に下がるように描画される。
線は斜め上に上がってほしかったので、while
の結果全体をtac
で反転されている。
tac
はcat
の逆で、入力データの最終行から最初の行に向かって出力するコマンド。
$ seq 10 \ | while read i; do printf "%${i}s\n" '/' done \ | tac \ | sed 's/ / /g' \ | align left -p ' ' \ | sed -r 's/^/|/g' | / | / | / | / | / | / | / | / | / |/
難読化うんこ
超絶読みづらくしたunko.tower。解説するのがしんどい。
+() { ! :;bc<<<obase=$?$#\;ibase=$(($?<<$?))\;${@:${?}:${?}}; }; $(echo -e "\x`! :;+ $?$?$?``! :;+ $?$?`\x`! :;+ $?$?$#``! :;+ $?$#$?`\x`! :;+ $?$?$#``! :;+ $?$#$#`") `! :`$((($?<<$?)+$?))$(echo -e "\x`! :;+ $?$?$?``! :;+ $?$?`")/ / ・__・ / <(unko.tower) #シェル芸 人 ( ) ( ・__・ ) ( )
+()
はただの関数定義。関数名を+
としている。
中でやっているのはbc
コマンドに<<<
(ヒアストリング)でbc用の式を渡している。
+
の関数内の先頭の! :;
は$?
を1にするための処理。
$(($?<<$?))
はbashの算術演算式で、左シフトを表現している。
よって$(($?<<$?))
は$((1<<1))
になり、1の1左シフトで2になる。
あとのecho -e
のところは、16進数で表現したsed
コマンド。
ただし直接sedとしたくなかったので、echo -e
で16進表現(\xFF
みたいな)を文字に変換している。
あみだ線生成
あみだくじを生成する。 アルゴリズムが適当なので総当りで文字を生成して、余計なものが混じってる行を出力しないようにしているので 計算量は増えがち。 あみだくじの線の数を増やしたら出力されないほうが多くなる。
make_row() { seq $1 \ | xargs -I@ bash -c 'printf "$(shuf -n1 -e ├ ┤ │)"' \ | grep -vEe "├([^┤]|$)" -e "(^|[^├])┤" return $? } row=5 col=6 n=0 while [ $n -lt $row ]; do ! make_row $col n=$((n+$?)) done
├┤├┤││ ││││├┤ ││││├┤ ├┤│├┤│ │├┤├┤│
やってることとしては、make_row
関数であみだくじを生成し、その戻り値で
あみだくじが生成されたかどうかを判定し加算し続けるというもの。
指定の数になったらwhile
を抜ける。
grep
は指定の文字が1つも見つからなかったときに終了コードが1になる。
1つでも文字が見つかった場合は終了コードが0になる。
今回のケースではその性質を利用し、あみだくじが出力されたとき、 「1行あみだくじの行を生成した」ということとして 生成した行数1を戻り値として返すということをやっている。
呼び出し元で!
を使って0を1に反転しているので、
grepで行が見つかった際に1加算している。
(不正なくじが生成されていない)
終了コードの使い方としてはよろしくないアプローチだとは思う。
虹色のうんこ
super_unkoのunko.printpnm
という自作のコマンドで出力したうんこのテキストを虹色に変換している。
PNMについてはWikipediaが参考になる。
a=("255 0 0" "255 165 0" "255 255 0" "0 128 0" "0 255 255" "0 0 255" "128 0 128") i=0 while read -r line do i=$((++i)) sed -r 's/255 255 255/'"${a[$((i%7))]}"'/g' <<< "$line" done <<< "$(unko.printpnm)" \ | convert - -scale 200x200 /images/t.png #シェル芸
whileに対してヒアドキュメントを渡すところから処理が始まる。
パイプでwhileに渡していないことには理由があって、i
というループカウンタを加算したいから。
パイプ後で実行されるコマンドは別シェルとして起動されるため、 その中で変数を定義したり値を変更しても、while外の変数には影響を与えない。
ループカウンタi
は0〜6の値をループさせたいのでパイプは使えない、ということで
ヒアドキュメントで渡している。
ループカウンタは虹色のRGBを定義した配列から色を取り出すときに使用している。 sedでもとの色の指定の箇所をループの都度sedの式を配列の色をもとに生成して置換している。
convert -
ではパイプの結果を入力データとして利用することを指定している。
それをPNGとして保存する。
ちなみにwhileにテキストを渡す方法にはいくつかアプローチがある。 ファイルをいきなり渡したりコマンドの実行結果を渡したりパイプで渡したり いろいろ方法がある。 いくつかやり方を知っていると芸の幅が広がる。以下はその一例。
パイプせずにファイルをwhileに渡す
while read -r line; do echo $line done < text.txt
パイプしてファイルを渡す
cat text.txt | while read -r line; do echo $line done
パイプせずに複数行のテキストを渡す
while read -r line; do echo $line done << EOS 1 22 333 EOS
パイプせずにコマンドの実行結果を渡す
while read -r line; do echo $line done <<< "$(seq 3)"
色のついたテキストを画像に変換する
手前味噌で恐縮だが、textimgという自作のコマンドを使う。 これは渡されたテキストのエスケープシーケンスを解析してRGB値に変換して画像出力するコマンド。 シェル芸bot環境で画像を描画したくて作った。
bigunko.show | textimg -o /images/t.png
シェル芸bot環境では以下のように短くかける。
bigunko.show | textimg -s
grepの結果やscreenfetchなどの結果も画像に反映できる。
screenfetch | textimg -s
ちなみに上にスクロールできるオプションがある。
殺意のうんこ
前述のbigunko.showの色を変更して赤黒い配色に変更するシェル。
bigunko.show \ | sed 's/100/0/g;s/94/16/g;s/58/1/g;s/243/0/g' \ | sed 's/\x1b\[0m$//g;:a;s/ /う/;s/ /ん/;s/ /こ/;/ $/ba;s/[うんこ]/\x1b[31m&/g' \ | textimg -s #シェル芸
bigunko.showの結果をlessで確認してみるとESC[48;5;243m
とかって文字が大量に表示される。
これは端末上で背景色の変更を表現するエスケープシーケンス。
ESC[4n
から始まると背景色、ESC[3n
から始まると文字色が変わる。
このシェルでやっているsed
はこのエスケープシーケンスの数値を別の色の数値に置換する。
置換が終わったら全角空白文字をうんこ文字に置換する。
文字色を変更するためESC[31
を文字の置換に指定する。
色のエスケープシーケンスについて
前述のESC[48;
とかって文字を端末上で再現する場合は\x1b[48
あるいは\e[48
と書く。
色だけ試したい場合は以下のシェルを実行してみるととりあえず色は見れる。
for i in 3 4; do for j in {0..7}; do echo -ne "\e[${i}${j}m test \e[m" done echo done
ESC[3n
は文字の色を変更する。
ESC[4n
は文字の背景色を変更する。
n
の値は0~8になる。0~7については色が固定だが、8を指定するとさらに別の色を指定できるようになる。
0~7の色についてはQiitaの記事 とかを見ればすぐに見つかる。
で、8の指定は特殊。 8を指定した場合は2通りの色の指定の仕方がある。 書式は以下の通り。
分類 | ESC | ESC | 色指定 | 例 |
---|---|---|---|---|
文字色 | ESC[38 (固定) | 5 (固定) | 0 ~ 255m | ESC[38;5;128m |
背景色 | ESC[48 (固定) | 5 (固定) | 0 ~ 255m | ESC[48;5;128m |
0〜255の色の一覧は下記の通り。
もう1つはRGB指定する方法。
きもいオジサンのLINEメッセージ
先日(2019/05/31だったかな)ojichatというコマンドがシェル芸bot環境に追加された。 これはLINEやTwitterなどのSNSで女性に対して下心丸出しのおっさんが送るメッセージを再現するコマンド。 以下のように実行する。
ojichat
ヤエ、そっちも雨なのかな( ̄ー ̄?)⁉出張で広島に行ってきたよ😍💕😄観光でも、行きたいなぁ💗💗モチロン、ヤエとネ
ojichat 花子
花子チャン、お疲れ様〜😃(^o^)😃✋今日は長崎28度だよ😱💦^^;💔暑いよ^^;💔ヤケドしないように気をつけないトネ(^_^)😃☀ (笑)
文章は完全にランダムに生成されるので、特定のパターンがほしいときはheadする。 以下は10回ojichatを実行する。
yes ojichat | head | bash
これを利用して特定の文言が出現するパターンを取得して文章を書き換えて独自の文章を作ったり。
メンヘラおじさん。
m=$(seq 100 | xargs -I@ printf "なんで") yes ojichat \ | head -n 100 \ | bash \ | grep こうよ \ | head -n 1 \ | sed -E 's/こうよ.*/こうよ・・・。行かないの?なんで・・・'$m'/g' #シェル芸
マキちゃん、オハヨウ〜😚😃😃♥ ちょっと電話できるカナ✋❓⁉⁉そろそろご飯行こうよ・・・。行かないの?なんで・・・なんでなんでなんでなんでなんでなんでなんでなんでなんでなんでなんでなんでなんでなんでなんでなんでなんでなんでなんでなんでなんでなんでなんでなんでなんでなんでなんでなん
別の文章と合わせたりして色々遊べる。
リョウちゃん、今日もお仕事かな🎵おじさんと一緒に今度ランチ、したいなぁ😄😃☀ 😃☀ (^_^)よく頑張ったね😍😃☀ 😆😃☀ えらいえらイ(笑)
— シェル芸bot(停止しました) (@minyoruminyon) 2019年5月31日
(その3分後・・・)
サポートしてくれうあぁ!ぎゃぁぁ!!誰か来てくれ!援護が必要だ!被弾しました救助してくれ!助けてくれーーっ!ここにいる! https://t.co/VpU8e04jsM
きもいオジサンのメッセージをうんこに埋め込む
殺意のうんこシェル芸と同じようなことをやっている。
https://t.co/uB3IKRGFSo pic.twitter.com/A4pvLzyYGi
— シェル芸bot(停止しました) (@minyoruminyon) 2019年5月31日
bigunko.showに含まれる文字を埋め込むには少し工夫が必要。 bigunko.showの全角スペースの前後にはエスケープシーケンスがついているため 「全角文字の連続」みたいな正規表現はかけない。
今回の僕のアプローチは「sedの置換式文字列をループで回し、bigunko.showが書かれた テキストを都度上書きして1文字ずつ全角空白を置換する」というようにした。
前述のシェルを分解すると以下のような3つのシェルに分解できる。
bigunko.show \ | tr '\n' 'Q' > a.txt for __ in {1..20} do for cmd in $(ojichat | grep -o . | sed -E "s,.,s/ /&/ ,g" | tr -d "\n"); do sed -i "$cmd" a.txt done done cat a.txt|tr Q \\n \ | sed -E 's@\x1b\[48;5;243m@\x1b[41m@g;s@.*@\x1b[30m&\x1b[m@g'\ | textimg -s #シェル芸
まずbigunko.showをテキストファイルに出力する。 このとき改行文字を元の文章に出現しない何でも良い文字に置換して1行のデータに変換している。 sedのループで置換するときに1行のデータのほうが処理しやすいため。
bigunko.show \ | tr '\n' 'Q' > a.txt
sedで文字を置換する処理。
sed -E "s,.,s/ /&/ ,g"
の箇所でsedの式を生成している。
for __ in {1..20} do for cmd in $(ojichat | grep -o . | sed -E "s,.,s/ /&/ ,g" | tr -d "\n"); do sed -i "$cmd" a.txt done done
sedの結果を一旦出力すると次のようになる。
% ojichat | grep -o . | sed -E 's,.,s/ /&/ ,g' | tr -d \\n s/ /は/ s/ /つ/ s/ /か/ s/ /チ/ s/ /ャ/ s/ /ン/ s/ /、/ s/ /ヤ/ s/ /ッ/ s/ /ホ/ s/ /ー/ s/ /(/ s/ /^/ s/ /o/ s/ /^/ s/ /)/ s/ /😃/ s/ /😃/ s/ /♥/ s/ / / s/ /😄/ s/ /何/ s/ /し/ s/ /て/ s/ /る/ s/ /の/ s/ /か/ s/ /い/ s/ /❓/ s/ /早/ s/ /く/ s/ /会/ s/ /い/ s/ /た/ s/ /イ/ s/ /ナ/ s/ /😄/ s/ /💕/
sed
の&
はマッチした文字列をそのまま埋め込む特殊な値。
後方参照みたいなもの。よく使う。
この結果をcmd
にセットし、sed -i
の式として利用している。
-i
オプションでファイルを置換した結果で上書きできる。
分解すると以下のようなsedとして実行されている。
sed -i "s/ /は/" a.txt sed -i "s/ /つ/" a.txt
最後に結合。最初の処理で1行のデータにしていたものをもとに戻す。 sedの式では、bigunko.showの背景色を赤色のエスケープシーケンスに置換している。
cat a.txt|tr Q \\n \ | sed -E 's@\x1b\[48;5;243m@\x1b[41m@g;s@.*@\x1b[30m&\x1b[m@g'\ | textimg -s
あとこのシェルだけでなくシェル芸botではよくやってしまうが、
sed
で複数の式を一気に書いている。
sed -E 's@\x1b\[48;5;243m@\x1b[41m@g;s@.*@\x1b[30m&\x1b[m@g'\
圧縮されていて読みにくいが、複数のsコマンドをここでは実行している。
;
が式の区切り文字である。
実はあまり知らない人多いと思うが、sed
は複数の式を同時にかけるし、何なら複数行に書くこともできる。
echo あいう | sed 's/い/i/; s/i/Z/'
あzう
これも結局は以下のシェルと同じである。
% echo あいう | sed 's/い/i/ s/i/Z/ '
あZう
割とあまり一般的でない特殊なシェルを書いたような気がする。(シェル芸では一般的 なテクニックかもしれないが、普通のサーバオペレーションではまずやらないようなシ ェルの使い方であることは間違いない)
以上です。