たいちょーの雑記

ぼくが3日に一度くらい雑記をかくところ

第49回シェル芸勉強会に参加しました

前回に続きリモートシェル芸勉強会でした。今回は問題は簡単だけど解答を変態にしなさいという感じらしい。変態じゃないのでわからんね。

問題と解答はこちら。当日のライブストリームはこちら

今回もbash/zshPowerShellの解答を書いていきます。PowerShellの方は変態度があまり高くないです。

Q1

「あ」と出力する問題。簡単にいくなら echo あとするわけですが、これでは変態度が低い。らしい。

$ seq 3000000 | factor | awk 'NF==2' | awk 'NR==1||NR==2||NR==182043{print $2}' | paste -sd '*' | sed 'iobase=16' | bc | sed 's/./\L&/g' | xxd -ps -r

はい。最中は提出できなかった・・・(凝りすぎた)

$ echo -n あ | xxd --ps | xargs -I@ printf "%d
" 0x@
14909826

「あ」を10進数で表すと14909826なので、これを素因数分解します。

$ echo -n あ | xxd --ps | xargs -I@ printf "%d
" 0x@ | factor
14909826: 2 3 2484971

これらの数値をseq | factorから取り出して行きます。

$ seq 3000000 | factor | awk 'NF==2' | grep -nP "^(2484971|2|3):"
1:2: 2
2:3: 3
182043:2484971: 2484971

1と2と182043行目に欲しい数字があるので、これを取り出します。

$ seq 3000000 | factor | awk 'NF==2' | awk 'NR==1||NR==2||NR==182043{print $2}'
2
3
2484971

これを*で連結して、評価してxxdで元に戻します。

PowerShellでの解答も行きます

@(2,3,29,71) | ForEach-Object { $agg=1 } { $agg*=$_ } { [char]$agg }
あ

まぁこれもやってること同じです。

Q2

ls -lを使わず、ファイル + タイムスタンプを表示する問題。ls -l が使いたい。 これはstatを使えばいいということですねえ

$ mkdir aaaa && cd aaaa && touch {a,b,c,d}
$ git init && git status -s | awk '{print $NF}' | xargs -I@ bash -c 'echo @ $(stat -c %y @)'

ファイルのリストアップにgit statusを使ったのですが、普通にecho *とかでもよかったですね。

PowerShellでの解答も行きます。

$ gi ./* |%{ $_.Name +" "+ $_.LastWriteTime  }

giGet-Itemへのエイリアスです。パイプで渡ってきたオブジェクトからプロパティを取り出しているだけですね。

Q3

seq 30からはじめて、3のつく数値だけを出力する問題。いつもはgrep 3で終わるけれど、これは変態度が低いんですね。慣れてきました。

$ seq 30 | perl -nle 'print if /3/'
$ seq 30 | csharp -e 'var x="";while((x=Console.ReadLine()) != null) if(x.Contains("3")) Console.WriteLine(x);'
3
13
23
30

解説するまでもないですね。どちらも愚直な実装だと思います

PowerShellでの解答も行きます。こっちもSelect-String 3とするだけなんですが、変態度がどうのこうの。

$ 1..30 | ?{[System.Text.RegularExpressions.Regex]::new("3").Match($_).Success}
3
13
23
30

?Where-Objectへのエイリアスです。ブロックを評価してtrueなものだけを通します。[System.Text.RegularExpressions.Regex]正規表現を扱うクラスです。これの正規表現な文字列を与えて、Matchメソッドで$_を評価していきます。今回は単に3と渡しているので3が含まれているとマッチするんですね。

Q4

echo 3+4+5+6からはじめて式を評価する問題。普通にやるならbcに渡せばいいですね。

$ echo 3+4+5+6 | bc
18

でもこれは変態的でないので、なんとか変態的にしていきます

$ echo 3+4+5+6 | tr + \n | jq -s add
18

trを使って+を改行に置換し、jqで足し算します。このjqでの足し算好きなのでよく使います。

PowerShellでの解答も行きます

$ echo 3+4+5+6 | iex
18

iexInvoke-Expressionへのエイリアスです。渡された文字列を評価します。まぁevalだと思っていいと思います。PowerShellからみると普通ですね。ちょっとjq的な答えも考えてみます。

$ (echo 3+4+5+6) -split '\+' | measure -sum | %{$_.Sum}
18

measureMeasure-Objectへのエイリアスです。パイプラインで渡ってきたりしてものをなんかいろいろ数え上げてくれたりします。オプションなしだと個数だけを数えますが、-sumを付けると、総和を計算してくれます。

Q5

echo てぶくろからはじめてろくぶてを出力する問題。普通はrevすればいいですね

$ echo てぶくろ | rev
ろくぶて

でもこれは変態的でないので、なんとか変態的にしていきます

$ echo てぶくろ | grep -o . | sed 's/./"&"/' | paste -sd , | sed -E 's/.+/[&]/' | jq reverse --indent 0 | tr -d ',"[]'
ろくぶて

jqreverseを使って反転させることにしました。jqJSONを扱うツールなのでてぶくろJSONの配列に加工します。

$ echo てぶくろ | grep -o . | sed 's/./"&"/' | paste -sd , | sed -E 's/.+/[&]/'
["","","",""]

これをjq reverseで反転させます

$ echo てぶくろ | grep -o . | sed 's/./"&"/' | paste -sd , | sed -E 's/.+/[&]/' | jq reverse --indent 0
["","","",""]

いらないところをtrで削って終わりですね。

PowerShellでの解答も行きます・・・が、まぁこれは普通だな・・・

$ echo てぶくろ | %{[System.Linq.Enumerable]::Reverse($_) -join ""}
ろくぶて

普通過ぎるので別の方法をとります。せっかくPowerShellでは.NETのクラスが使えるので、それを使ってみたいと思います。

$ (echo てぶくろ) -split "" | % {$st=New-Object System.Collections.Stack} {$st.Push($_)} {$st -join ""}
ろくぶて

System.Collections.Stackです。説明するまでもないですがStackを扱うオブジェクトです。Stackに順番に入れて取り出せば逆順になりますね。

Q6

数字を使わず素数を10個ほど出力する問題。数字を使うなら seq inf | factor | awk 'NF==2{print $NF}' | headこんな感じですね

c++を使ったりしてエラトステネスを実装しようかと思ったんですが、単純に2 3 5 7みたいな感じで出力すればいいんだなあってなりました。もちろん数字のところは難読化とかが必要ですが。頭…柔らかくしていこうな!

Q7

数字を使わずに0~9を出力する問題。普通なら seq 0 9でいいですね

$ seq $? $((-~-~-~-~-~-~-~-~($$/$$)))
0
1
2
3
4
5
6
7
8
9

$?は前のコマンドの終了値です。前のコマンドが正常終了していれば$?は0になります。$((-~-~-~-~-~-~-~-~($$/$$)))はややこしく見えますが、単項演算子を連続でつけているだけです。-は負に、~はビット反転です。$$/$$のうち$$はプロセス番号です。プロセス番号をプロセス番号で割ると1なのでこの式は-~-~-~-~-~-~-~-~1を評価することになります。この結果は9になるので、seq 0 9が評価されますね

別解も用意しました。これは前回の勉強会で出た問題の再利用です。詳しくは説明しませんが、スクリプト再帰している感じですね。

$ echo 'echo $((SHLVL-$$/$$)); bash -c $'$? > x && chmod +x x && ./x | head

PowerShellでの解答も行きます

$ [int]!$?..("やっほーうんこだよ").Length
0
1
2
3
4
5
6
7
8
9

やっほーうんこです。せっかくPowerShellなので、PowerShellにあるものでやってみました。

$ 0..9
0
1
2
3
4
5
6
7
8
9

これで範囲内の数値を出力できます。こいつを数字を使わずにやります。まず0は[int]!$?です。$?bash/zshなどとおなじで前のコマンドの終了ステータスが入っています。正常ならTrueが入っているので、!Falseにしてintにキャストすると0になります。

9のほうは計算で出してもいいんですが、せっかくオブジェクトにメソッドが生えているので使いたいです。ちょうど「やっほーうんこだよ」が9文字なのでこれを使いました。Lengthで長さを取得できます。めでたく9が得られました。

Q8

unkoファイルは以下のようになっています

んこうんこううんうこうんんこうこんんこう
んこここんこうこうんこうこうんんこうんこ
うこううんうここうこんんんうんううんんこ
んんんこここううううんこんこんうこうこう
んこうんんんううこううううこうここうんん
んここうこんうここうんううんこうんこうう
うこうんここここんんうこここここここんこ
うこうこここうんんううううんここうこんう
ここうんうんんんんこうこんううんんこんう
んうううここんここここううこんんんうんう
んこうんんこんううんうんこんんうんこんん
んんうここうこううこううんんうこうこうん
こううこううここんんこんううんうんんんう
こうこうんんんこんこんんううんんんこんこ
んんんこうんうこうここうんこここうんこん

ここからうんこが含まれる行番号だけを出力する問題です。ただし、ワンライナー中に日本語を使ってはいけないとのこと。今回はうんこないと思ったけどやはり

ちなみにどんな感じにうんこが分布しているかはこんな感じ

$ cat unko | grep -n うんこ
1:んこうんこううんうこうんんこうこんんこう
2:んこここんこうこうんこうこうんんこうんこ
4:んんんこここううううんこんこんうこうこう
6:んここうこんうここうんううんこうんこうう
7:うこうんここここんんうこここここここんこ
8:うこうこここうんんううううんここうこんう
11:んこうんんこんううんうんこんんうんこんん
15:んんんこうんうこうここうんこここうんこん

でも「うんこ」を使っているのでダメですね。

$ cat unko | awk "/$(uconv -x hira <<< unko)/{print NR}"
1
2
4
6
7
8
11
15

uconv -x hira <<< unkounkoをひらがな読みに変換します。これはまさかなんですがちゃんと「うんこ」とでます。うれしいですね。というわけでこのワンライナーは以下のようになります

$ cat unko | awk '/うんこ/{print NR}'

はい。

PowerShellでの解答も行きます

$ cat unko | sls ((@(58,135,71) |%{[char]($_+12300)}) -join "") | %{$_.LineNumber}
1
2
4
6
7
8
11
15

うむ・・・。なんか面白い文字の難読化を見つけたいですね

LT

ocsというopyのC#版みたいなのを用意していたんですが、いろいろ事故ってLTできませんでした。モノ自体は完成しているので、そのうちブログ書きますね

終わりに

今回は問題としては簡単だったのか、いろんな解答が見られてとても面白かったです。ただなんかやっぱり疲労感は感じますね。次回も楽しみです!!!開催ありがとうございました!!!

第48回シェル芸勉強会に参加しました

しました

今回は前回に続きリモート開催ということで、おうちから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 は最後に現れるので、$NFxargsechoを作って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 をバックグラウンドで動かしておく。呼び出し元のサブシェルはそのまま終了するのでsleepinitとかにぶら下がるようになる。ということらしい

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

$SHLVLseqコマンドを作る問題。

$ 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 

という風になる。このうち真ん中のsleepexecbashから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をしてくださったお二人はどちらも超良かったのでぜひアーカイブをご覧ください。

終わりに

前回はフル参加できなかったので久しぶりのシェル芸勉強会でした。いつもとは少し毛色が違う問題があってとても楽しかったです!ありがとうございました!!!

雑記 2020-05-06

雑記

動かした方が痛くないなぁ

置き配

はじめて置き配を経験した。8時ぐらいに目覚めてメールを確認したら、「置き配しました☆ミ」というメッセージが写真付きで届いていた。その写真には我が家の玄関に荷物が置いてある様が写っていた。すげー時代だなぁと思った。

Surface3のOSをWindowsにもどした

もどした。このために16GBのUSBメモリも購入した。まぁ前から欲しかったからいいのだけど。
Windowsに戻した感想は、やはりというかなんというかWindows機だよな!って感じ。Linuxを入れていた時はWi-Fiがすぐ死んだり、バッテリー情報が取得できなかったり、ゴーストタッチが発生したりしてたんだけど、そういうこともなく元気に動いてくれて私は嬉しい。用途はドット絵とYouTubeだからセットアップもすぐ終わった。セキュアブートは戻してないのでそれだけはやっておかないとね。

ところでこれはもうスペックの問題だと思うんだけど、TweetDeckがめちゃめちゃ重い。Twitter離れが加速する。新しいSurfaceが発売されたら買おうかなぁ…

アニメを見た

アニメを見るときってめちゃくちゃエネルギーを使うので、普段は見ない。見ても1本だが、それはプリキュアに割いているので他のは見ることができない。ところが、このGWは手首が痛いWeekだったので、手を動かさずできることとしてアニメを見ようとなった。選んだのは完全未履修のエヴァバビロニアエヴァYouTubeで公開されている分。バビロニアAmazon Primeでみた。エヴァはよくわからなかった。バビロニアは金ぴかがカッコよかった。ただどちらも作中でめちゃめちゃ人が死ぬので、次はゆるふわなアニメにしたいと思う。

おわり

なんだよGW…はは…まだ始まったばかりだろ…?な?いかないでくれよ…なぁ…!

雑記 2020-05-01

雑記

いてて

腱鞘炎

仕事柄というか趣味のせいだかわからないけど腱鞘炎になりました。こまったなぁ

家具屋

近くに家具屋がある。部屋に照明がなかったので買いに行った。ついでにキッチンマットとかメタルラックなんかを買い足した。当然車なんて高価なものは持っていないから持ち帰りは手。重いからしんどいというより、箱がでかすぎて持てないっていうね

おわり

owari

雑記 2020-04-28

雑記

海鮮丼が食べたい

Surfaceの続き

Ubuntu20.04をSurface3にインストールした。今の所動いているけど、前々から不調だったWi-Fiがいよいよもって厳しくなってきた。起動した直後はまだいいんだけど、暫く経つとデバイスすら認識されなくなる。あとでSurface-Linuxのカスタムカーネル試そうと思ってる。

今は古いAndroidをつないでテザリングさせてる。まぁこれでも良いかという気はしている。

おわり

終わりです

雑記 2020-04-27

雑記

誤字訂正能力のないチャットツールさん

さーへす

最近Youtubeを見るために使っていたタブレットが壊れてしまった。今は古いAndroidを取り出してそれで見ているんだけど、やはり画面の小ささが気になってしまい、別のを探していた。そこで見つけたのがManjaro入りのSurface3。ブラウザで見ることになるから広告もカットできていいなぁと思って起動したんだけど、なんとこの端末、音が出ない。alsamixerでデバイスを確認しても、このデバイスにはコントロールがありませんと言われる。なんだよ…。
頑張ってトラブルシュートしようかと思ったのだけど、正直IMEWi-Fiも信じられないほど不安定なのでOSを別のにしたほうが良いんじゃないかと…。というかWinでよくないか…?

おわり

月曜日って感じ

雑記 2020-04-24

雑記

朝起きて、窓を開けるの。そしたら部屋が寒いの。

CLIでコピーをしたい

CLIでSTDINからの入力や、一つ前のコマンドをコピーしたいことがあるのでShell関数を定義している

# copy text to clipboard
# usage:
#   - yy [text] -> copy [text] to clipboard
#   - [command] | yy -> copy [command]'s output to clipboard
#   - yy -> copy last command
function yy() {
  local text="${*}"
  [ -p /dev/fd/0 ] && text=$(cat -)
  [ "$text" = "" ] && text="$(history | tail -n1 |cut -d' ' -f4-)"

  # wsl
  type clip.exe 2>&1 > /dev/null && echo -n "$text" | clip.exe && return 0
  # xclip
  type xclip 2>&1 > /dev/null && echo -n "$text" | xclip -selection c && return 0
  # xsel
  type xsel 2>&1 > /dev/null && echo -n "$text" | xsel -b && return 0
  # pbcopy
  type pbcopy 2>&1 > /dev/null && echo -n "$text" | pbcopy && return 0

  logger.warn "clip.exe, xclip, pbcopy or xsel not found"
}

clip.exeかxsel,xclip,pbcopyがあれば使える。文章の最後の改行をトリムすべきかどうかは考え中。

fujitastulize

function fujitatsulize() {
  cat | sed 's/./&゛/g'
}

$ echo どうしてなんだよおおおおおおお|fujitatsulize
ど゛う゛し゛て゛な゛ん゛だ゛よ゛お゛お゛お゛お゛お゛お゛お゛

おわり

今週も終わる