Slide 1

Slide 1 text

シェル芸のせかい Ryoto (@systemctl_ryoto) 2020/03/09 Uedalab Meetup 1

Slide 2

Slide 2 text

こんにちは 今日はシェル芸のお話をします $ echo こんにちは $ echo すましを話おの芸ルェシは日今 | rev 2

Slide 3

Slide 3 text

$ w3m https://ja.wikipedia.org/wiki/シェル 芸 | grep ^シェル芸 -A 5 シェル芸[編集] シェル芸とは、主にUNIX系オペレーティングシステムにおいて「マ ウスも使わず、ソースコードも残さず、GUIツールを立ち上げる間 もなく、あらゆる調査・計算・テキスト処理を CLI端末へのコマンド 入力一撃で終わらせること」(USP友の会会長・上田隆一による定 義^[9])である。この技術を持つ人物を指すシェル芸人という呼び 方も存在する^[10]。 3

Slide 4

Slide 4 text

シェル芸モチベーション ● コマンド実行を効率化する ● シェルの機能を最大限利用する ● やりたい動作を素早く実現する ● とっさの時のサーバトラシュー力が向上する ● GUIよりCLIの方が軽量、ソフトウェアも多い[要出典] 4

Slide 5

Slide 5 text

シェル芸とその基礎:標準入出力(stdio) 5

Slide 6

Slide 6 text

シェル芸を始める前に:標準入出力 ● 入出力の抽象 ● 標準入力(0)、標準出力(1)、標準エラー出力(2) ● ファイルディスクリプタ番号は固定 ● デフォルトはターミナル(tty)にバインドされている ● リダイレクト(<, >, <<, >>, etc.)によってファイルなどへ変更可 能 シェル芸とその基礎:標準入出力 (stdio) 6

Slide 7

Slide 7 text

$ echo terminal >$(tty) $ echo stdout >/dev/stdout terminal # ターミナルへ直接出力 stdout # 標準出力へ出力(=何も指定しないのと一緒) 別の画面のttyに向けて echo hello >/dev/tty?? すると…? program /dev/tty?? /dev/stdout シェル芸とその基礎:標準入出力 (stdio) 7

Slide 8

Slide 8 text

$ echo うっしっし | cowsay # pipe “|” は前のプロセスのstdoutと後のstdinをつなげる # これで立派なシェル芸人 _________________ < うっしっし > ----------------- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || pipe stdout echo stdin cowsay シェル芸とその基礎:標準入出力 (stdio) 8

Slide 9

Slide 9 text

シェル芸の沼にハマると ● シェルでCMSを作って運用し始める https://github.com/ryuichiueda/bashcms2 ● シェルでHTTPサーバを作り始める https://github.com/ryotosaito/shttpd ● 少ない記号種でdateを実行するゴルフ?が始まる https://twitter.com/minyoruminyon/status/1152161173158055936 ● シェル芸をTwitterに投下できるbotを作る https://github.com/theoremoon/ShellgeiBot ● VUIでシェル芸をしたくなった結果呪文詠唱を始める https://speakerdeck.com/amanoese/vuidesieruyun-woshi-xing-dekiruyounisitemita 9 シェル芸とその基礎:標準入出力 (stdio)

Slide 10

Slide 10 text

$ echo fd1 >/dev/fd/1 $ echo alias >&1 fd1 # ファイルディスクリプタ1 (= stdout) に出力 alias # >&1 は >/dev/fd/1のエイリアス まとめ:標準出力の表し方 ● /dev/stdout ● /dev/fd/1 ● >&1 シェル芸とその基礎:標準入出力 (stdio) 10

Slide 11

Slide 11 text

$ echo stderr >/dev/stderr $ { echo error >&2; } 2>error.txt stderr # 標準エラー出力 → ttyに出力 # “error” を標準エラーに出力 → さらに error.txt に転送 # 標準出力も標準エラーもデフォルトはttyにバインド シェル芸とその基礎:標準入出力 (stdio) 11 program /dev/tty?? /dev/stdout /dev/stderr /dev/tty?? error.txt

Slide 12

Slide 12 text

$ man life 2>&1 | cowsay $ man life |& cowsay 標準エラー出力をpipeしたい場合は、 ● プロセス中で標準出力にリダイレクトする ● 標準エラー出力も一緒にpipeする ’|&’ を使う __________________________ < No manual entry for life > -------------------------- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || シェル芸とその基礎:標準入出力 (stdio) 12

Slide 13

Slide 13 text

シェル芸の基礎:シェル機能 13

Slide 14

Slide 14 text

コマンド出力をコマンド文字列として認識する Command substitution $ docker rm $(docker ps -aq) # dockerのコンテナを全て削除する # docker ps -aqはコンテナIDをリストする # 展開対象は標準出力(stderrはttyに垂れ流される) シェル芸の基礎:シェル機能 14

Slide 15

Slide 15 text

$ paste <(seq 1 3) <(seq 4 6) 1 4 2 5 3 6 # equivalent to # seq 1 3 >13.txt; seq 4 6 >46.txt; paste 13.txt 46.txt $ echo <(seq 1 4) <(seq 4 6) /dev/fd/63 /dev/fd/62 コマンド出力をファイルとして認識する Process Substitution シェル芸の基礎:シェル機能 15

Slide 16

Slide 16 text

その他Bashの有用な機能 ● /dev/tcp/host/port でネットワークリダイレクト ○ exec 3<>/dev/tcp/localhost/80 ○ echo -e "GET / HTTP/1.1\r\n\r\n" >&3 ○ while read line <&3; do echo $line; done ● command1 || command2で失敗時処理 ● ((i++))などの演算や、for ((i=0;i<10;i++))の記述 ● declare -n や local -n で変数の参照を得る ● {a,{b,c}d}e → ae bde cde のようなブレース展開 ● ENV=param commandで、command実行時のみENVを変える シェル芸の基礎:シェル機能 16

Slide 17

Slide 17 text

闇の魔術 :(){ :|:&; };: 絶対に実行してはいけない(cf. forkbomb) function colon() { colon | colon& } colon 片方 (:&) がバックグラウンドプロセスになり、killできない プロセステーブルが飽和し、端末が死亡 閑話休題 シェル芸の基礎:シェル機能 17

Slide 18

Slide 18 text

シェル芸に関わる規範たち 18

Slide 19

Slide 19 text

UNIX哲学 一つのことを行い、またそれをうまくやるプログラムを書け。 協調して動くプログラムを書け。 標準入出力(テキスト・ストリーム)を扱うプログラムを書け。標準入出力 は普遍的インターフェースなのだ。 https://ja.wikipedia.org/wiki/UNIX哲学 シェル芸に関わる規範たち 19

Slide 20

Slide 20 text

POSIX ● Portable Operating System Interfaces ● https://pubs.opengroup.org/onlinepubs/9699919799/ ● Xは?→ UNIXからきてる ● C標準ライブラリやシェル(sh)の仕様が統一されている ● 基本的なコマンド(Utilities)も載っている ○ awk, cd, chmod, ls, sed, xargs, 他たくさん ● 拡張シェル(bash, etc.)は(基本的に)これを守ってる ● POSIXの仕様を守っている限りどの環境でも動くはず! シェル芸に関わる規範たち 20

Slide 21

Slide 21 text

sedとawkはPOSIX準拠 現在でも幅広くsedやawkが使われている理由の一つ (もちろんテキスト処理に特化しているというのもある) ただしGNU sed/awkなど独自拡張を行っているものもある ● sed -r、sed -i、sed -y ● awkの各種強力な関数(丸投げ) 環境依存を気にするようになったら一歩沼に沈んだ証 シェル芸に関わる規範たち 21

Slide 22

Slide 22 text

GNU Coreutils 流石にPOSIXコマンドだけじゃ足りないことも多いので、 追加で存在を前提とされるコマンド群がある fmt, shuf, tee, seq, factorなど 一部コマンドは POSIX にオプション拡張したものもある Coreutilsをさらに拡張したMoreutilsもある シェル芸に関わる規範たち 22

Slide 23

Slide 23 text

シェルでできる便利なコマンド 23

Slide 24

Slide 24 text

$ { time sleep 1 &>/dev/null; } 2>&1 | tail -3 | awk '{print $2}' | xargs | tr ' ' , 0m1.052s,0m0.009s,0m0.021s # sleep 1を任意のコマンドに置き換えれば計測自動化できる # Bashのtimeコマンドの出力に依存している # for文や>>リダイレクトで複数条件計測とcsv錬成ができる シェルでできる便利なコマンド 24 $ time sleep 1 <空行> real 0m1.052s user 0m0.009s sys 0m0.021s | tail -3 | awk... 0m1.052s 0m0.009s 0m0.021s | xargs 0m1.052s 0m0.009s 0m0.021s

Slide 25

Slide 25 text

$ $(pbpaste) | pbcopy # macos $ $(xsel -b) | xsel -bi # Linux (X) # Clipboardのコマンドを実行した結果をClipboardに入れる pbcopy/pbpasteは標準で入っている xselはパッケージマネージャを通してインストール可能 シェルでできる便利なコマンド 25

Slide 26

Slide 26 text

CSI(Control Sequence Introducer) ESC(0x1b) [ のエスケープシーケンスを入力することで様々な端 末制御が可能となる 文字色・カーソル移動・文字削除・端末サイズ変更、etc. ● sh → printf '\x1b[31mRED\x1b[m' ● bash → echo -e '\x1b[31mRED\x1b[m' ● zsh → echo '\x1b[31mRED\x1b[m' RED # 表示設定(m):前景色(30)が赤(1)になる=31m もっと直感的に指示したい場合はtputコマンドがある 26 シェルでできる便利なコマンド

Slide 27

Slide 27 text

シェル芸人御用達コマンド・イディオム 27

Slide 28

Slide 28 text

# 整形コマンドとしてのxargs $ seq 12 | xargs -n 3 1 2 3 4 5 6 7 8 9 10 11 12 # 通常xargsは標準入力を最後尾に配置してコマンド実行する $ ls | xargs tar cvf dir.tar # が、コマンドを書かないとただの整形コマンドと化す $ echo -e "many spaces \tand\ttabs\!\!" | xargs many spaces and tabs!! シェル芸人御用達コマンド・イディオム 28

Slide 29

Slide 29 text

# 任意箇所に埋め込むxargs $ ls | xargs -I{} echo "There is {} here." There is Desktop here. There is Documents here. … # I(large i)オプションによって任意の場所に埋め込める # 直後に指定した文字列を置換する # 複数箇所指定も可能 シェル芸人御用達コマンド・イディオム 29

Slide 30

Slide 30 text

$ seq 10 | tr '\n' ' ' $ seq 10 | tr -d '\n' 1 2 3 4 5 6 7 8 9 10 # 改行をスペースに変換する 12345678910 # 改行を消滅させる # この方法は一番最後の改行(10\n)も対象なことに注意 シェル芸人御用達コマンド・イディオム 30

Slide 31

Slide 31 text

$ seq 10 | tee >(tac | xargs > rev.txt) | xargs | tr ' ' + | bc 55 # cat rev.txt -> 10 9 8 7 6 5 4 3 2 1 # teeコマンドは本来ファイルと標準出力へsplitさせる # Process Substitutionによって2つのコマンドに渡せる # 並行処理なので両方とも標準出力に流すと大変 # 環境を気にしないのならpeeコマンド(moreutils)が便利 $ seq 10 | pee "tac | xargs > rev.txt" "xargs | tr ' ' + | bc" シェル芸人御用達コマンド・イディオム 31

Slide 32

Slide 32 text

$ echo Helloworld | grep -o . H e l l o w o r l d # grepは基本的にマッチした行を抜き出す # -oはマッチした箇所のみを抜き出す # . は任意の1文字なので、全文字が分割表示される シェル芸人御用達コマンド・イディオム 32

Slide 33

Slide 33 text

$ yes 'echo $RANDOM' | head -3 | bash 28995 29287 23092 # コマンドを自力で生成してからpipe先のbashに投げて実行 # 複雑なコマンドを生成するもよし、 # ランダム要素のあるコマンドを何回も実行するときも便利 # seqをパイプすればfor文より見た目がエレガント[要出典] シェル芸人御用達コマンド・イディオム 33

Slide 34

Slide 34 text

$ shuf -ren10 {大,中,小,末,}吉 {大,}凶 # おみくじ10連打 ● -r 再抽選可 ● -e 標準入力ではなく引数をシャッフル ● -n10 10回出力 # Q. 本当に御用達なんですか? # A. 他に書くとこがなかった シェル芸人御用達コマンド・イディオム 34

Slide 35

Slide 35 text

$ sudo cat /var/log/auth.log | grep "Invalid user" | cut -f9 -d' ' | sort | uniq -c | sort -nr | head -10 # ssh不正ログインしてこようとしたusernameを暴く # Ubuntu 18.04で動作した # sort | uniq -c | sort -nr は最頻ランキングを作るのに便利 # cut -fnum -d' ' はスペースで区切られたnum番目を取得 シェル芸人御用達コマンド・イディオム 35

Slide 36

Slide 36 text

難読化 36

Slide 37

Slide 37 text

$ $(echo d)$’\x61’$(echo dGUK | base64 -d) Mon Mar 9 09:46:30 JST 2020 # それぞれ展開すると “date” になる # シェルは展開できるものを全て展開した後に解釈する ● echo d → d ● $'\x61' → a ● echo dGUK | base64 -d → te 難読化 37

Slide 38

Slide 38 text

難読化テクニックたち echo $[] → 0 ($[]は算術演算、何もないと0) echo ${##} → 1 ($#:引数の数=0、${#var}:文字列長) $(: shutdown -h now) (一瞬ひりつかせるが何もしない) /???/???/???1??? (/usr/bin/sha1sumを呼ぶ、環境依存) cut -c n (標準入力のn文字目を取り出す。切り貼りする) 難読化 38

Slide 39

Slide 39 text

ところで Connpassページのシェル芸、Bashで実行できます $ pbpaste | bash ShellGei!! 難読化 39

Slide 40

Slide 40 text

どういうこと $ pbpaste | bash -x でデバッグできる ___=$(.&>/???/??/${##}); ___に . &>/dev/fd/1 の出力を代入する .コマンドがエラーを返すので、それをfd1にリダイレクト -bash: .: ファイル名が引数として必要です .: 使用法: . filename [arguments] __=$? __に$?(直前コマンドの終了ステータス=2)を代入 難読化 40

Slide 41

Slide 41 text

つづき ! ___=${___##*.} ___に、___の最後の.以降の文字列を代入 -bash: .: ファイル名が引数として必要です .: 使用法: . filename [arguments] 言語環境に依存しない部分を抽出(このメッセージ自体はBashの builtinなので、Bashで実行している限り変わらない) 難読化 41

Slide 42

Slide 42 text

つづき ____=$(${___:$(($__$#-$?)):$?}${___:$__*$__:$?}${___:$ (($__$#-$__)):$?} -${___:$(($__#$?$?$#$?)):$?}&>/???/??/$?) 先ほどのコマンドで!をつけたので、$?が反転して1になる $#は0、これらで$___=' filename[arguments]'を切り貼り ____=$(set -g &>/dev/fd/1) bash: 1 行: set: -g: 無効なオプションです set: 使用法: set [-abefhkmnptuvxBCHP] [-o option-name] [--] [arg ...] 難読化 42

Slide 43

Slide 43 text

つづき これらを繰り返して最終的に ___=(eval $(echo {a..\u7a})) ___=(a b c d e … x y z) # ___はarrayになる を取得 配列インデックスをいじって任意コマンドが実行できる 難読化 43

Slide 44

Slide 44 text

さいごに 44

Slide 45

Slide 45 text

シェル芸勉強会 ● 2ヶ月に1回新宿で開催される勉強会 ● 界隈がfreakyで面白い(沼にハマった人たちを参考) ● シェルのワンライナーという制約下で8問解く 例えば: 日経のページから日経平均株価の日次データをダウンロードし て、毎月の終値の最高値と最安値を出力してください。 https://b.ueda.tech/?post=20191228_shellgei_45#q2 勉強会リンク(https://usptomo.doorkeeper.jp/) 45 さいごに