Upgrade to Pro — share decks privately, control downloads, hide ads and more …

シェル芸のせかい / the Shellgei World

シェル芸のせかい / the Shellgei World

Ryoto Saito

March 09, 2020
Tweet

More Decks by Ryoto Saito

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

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

    View Slide

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

    View Slide

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

    View Slide

  9. シェル芸の沼にハマると
    ● シェルで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)

    View Slide

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

    View Slide

  11. $ 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  15. $ 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

    View Slide

  16. その他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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  20. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  24. $ { 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

    View Slide

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

    View Slide

  26. 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
    シェルでできる便利なコマンド

    View Slide

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

    View Slide

  28. # 整形コマンドとしての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

    View Slide

  29. # 任意箇所に埋め込むxargs
    $ ls | xargs -I{} echo "There is {} here."
    There is Desktop here.
    There is Documents here.

    # I(large i)オプションによって任意の場所に埋め込める
    # 直後に指定した文字列を置換する
    # 複数箇所指定も可能
    シェル芸人御用達コマンド・イディオム 29

    View Slide

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

    View Slide

  31. $ 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  35. $ 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

    View Slide

  36. 難読化
    36

    View Slide

  37. $ $(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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  44. さいごに
    44

    View Slide

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

    View Slide