しました
今回は前回に続きリモート開催ということで、おうちからYouTube越しに参加。サテライトとは違い自分のうめき声しか聞こえないのでちょっと寂しいですね
問題と解答例はここ。今回はbashというかプロセスが云々の問題が多いようなので、PowerShellの解答はすくなめ
Q1
以下のようなスクリプトlineno
があります
echo $LINENO
これを使って、呼び出し元のLINENOを出力する問題。
そもそも$LINENO
は、行番号が格納されている変数。なので、これを普通に実行すると 1
が出力されて終了する。コマンドラインからecho $LINENO
すると、現在操作しているシェルの行番号が出力される。これをスクリプトを使って出力したい。
$ eval "$(cat ./lineno)"
はい。読みだしてそれをeval
すればよい。cat
を使わない方法としては
$ eval "$(< ./lineno)"
と書けるらしい。便利。
Q2
echo ${!BASH*}
とすると、BASH
で始まる変数が展開される一覧にある BASH_VERSION
の中身を出力する問題。
$ echo ${!BASH*} | awk '{print $NF}' | xargs -I@ bash -c "echo \${@}"
最初に答えたのはこれ。$BASH_VERSION
は最後に現れるので、$NF
、xargs
でecho
を作ってbash
に渡す。これを別の書き方をすると
$ echo ${!BASH*} > /dev/null && eval "echo \${$_}"
${!BASH*}
は展開されるので $_
にBASH_VERSION
が入る。echo \${$_}
という文字列を作る。これはecho ${BASH_VERSION}
になるのでeval
すれば欲しい文字列が得られる。
$ echo ${!BASH*} > /dev/null && echo ${!_}
TL眺めてていいなあと思ったのはこれ。${!_}
で変数関節展開をして出力する。最中は思いつかなかったなぁ。
Q3
sleep
をプロセス番号1のプロセスの下に3つぶら下げる問題
これは最中に解けなかった。やり方としては、子プロセスより親プロセスが先に死ぬと、子プロセスが親プロセスの親プロセスにぶら下がるようになるのを利用する。つまり、sleep
より先に呼び出した bash
が死ぬようにすればいい。
$ ( sleep 100 | sleep 100 | sleep 100 & )
sleep
をバックグラウンドで動かしておく。呼び出し元のサブシェルはそのまま終了するのでsleep
はinit
とかにぶら下がるようになる。ということらしい
Q4
今使っている端末上でecho $$
して1を出力する問題。$$
はプロセス番号を表す。なのでinit
とかsystemd
になろうって感じかなあ。と思ったらそういうことではないらしい
$ sudo unshare --fork --pid --mount-proc bash -c 'echo $$'
unshare
を使って名前空間を分割。そこで bash -c 'echo $$'
を実行すると、分割された名前空間でbash
がプロセス番号1として起動するので echo $$
で1が出力される。名前空間の分割はDockerでも使われている技術。SD誌を読もう!!!!!!
Q5
勝手にプロンプトにunkoと入力されるようにする問題。
なんだこの問題…と思っていたら時間が終わってた。解答としては exec
でSTDINを奪うというもの。kill
しないと止まらないので危険シェル芸
Q6
$SHLVL
でseq
コマンドを作る問題。
$ echo 'echo $SHLVL; bash -c "$0"' > s $ chmod +x s $ ./s | head
echo $SHLVL
で数字を出しつつ、$0
で再帰する。うむ
Q7
sleep
を三代ぶら下げる問題。Q3の応用
$ ( ( ( sleep 1000 ) & exec sleep 1000 ) & exec sleep 1000 ) &
なんでできたかわからん。簡単に言うと
sleep 100 & exec sleep 100
というようなシェルスクリプトを実行すると
bash + sleep + sleep
という風になる。このうち真ん中のsleep
はexec
でbash
からsleep
に変えられたもの。これを三段にするなら、これを呼び出すやつが呼び出したあと、exec sleep
すればよい
bash -c 'sleep 100 & exec sleep 100' & exec sleep 100
これでOK。これをサブシェルを使ってワンライナーにしたのが上の解答ということ
Q8
プロセスツリーで二分木を作る問題。forkbombに怯えてしまったのでやらなかったけど、実はこの問題が一番シェル芸っぽい問題とのこと
二つのプロセスを作るには
(bash|bash)
とすればいい。これを繰り返せばいいので以下のようにする
$ echo bash{,,,} | sed 's/bash/(&|&)/g' | sed 's/ /|/g;asleep 1000' (bash|bash)|(bash|bash)|(bash|bash)|(bash|bash) sleep 1000
これを bash
に評価させる。この時&
しておく。
$ echo bash{,,,} | sed 's/bash/(&|&)/g' | sed 's/ /|/g;asleep 1000' | bash & $ ps --forest 339 pts/0 00:00:00 \_ bash 400 pts/0 00:00:00 \_ bash 401 pts/0 00:00:00 | \_ bash 403 pts/0 00:00:00 | | \_ bash 413 pts/0 00:00:00 | | | \_ sleep 404 pts/0 00:00:00 | | \_ bash 402 pts/0 00:00:00 | \_ bash 405 pts/0 00:00:00 | | \_ bash 407 pts/0 00:00:00 | | \_ bash 406 pts/0 00:00:00 | \_ bash 410 pts/0 00:00:00 | | \_ bash 411 pts/0 00:00:00 | | \_ bash 408 pts/0 00:00:00 | \_ bash 409 pts/0 00:00:00 | \_ bash 412 pts/0 00:00:00 | \_ bash
おお、forkbombに怯えることなく二分木が出来た。すごいぜこれは
LT
今回は参加しませんでした。配信設備がない…ぐぬぬ。まあネタもなかったんですが…
LTをしてくださったお二人はどちらも超良かったのでぜひアーカイブをご覧ください。
終わりに
前回はフル参加できなかったので久しぶりのシェル芸勉強会でした。いつもとは少し毛色が違う問題があってとても楽しかったです!ありがとうございました!!!