たいちょーの雑記

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

第43回シェル芸勉強会大阪サテライトに参加しました

参加しました

暑かった

今回は大暴れ!ということで大暴れ!なシェル芸勉強会になりました(?)

問題と解答はここです

問1

echo あいうえお からはじめて あいいうううええええおおおおお という出力を得る問題。これはできそう

$ echo あいうえお|grep -o .|paste  <(req 5) -|-wk '{fod(i=0;i<$1;i++) pri\| $2}'|
あいいうううええええおおおおお

あいうえおを行番号とともに縦に並べて awk で処理するだけですね。行番号は awkNR でいいな…と後で気づきました

$ $("$(echo あいうえお)".ToCharArray()|% -begin{$NR=0} {"$_"*++$NR}) -join ''

こっちはPowerShellです。厳密には echo あいうえお から始まってないのでレギュレーション違反かもですが…。順番に解説します

# あいうえおをCharの配列にする
$ "$(echo あいうえお)".ToCharArray()
あ
い
う
え
お
# % => ForEach-Object でそれぞれを加工する
# -begin {} は awk の BEGIN{} と同じ
# PowerShellではstring型に対して掛け算をすると繰り返しになる
$ "$(echo あいうえお)".ToCharArray()|% -begin{$NR=0} {"$_"*++$NR}
あ
いい
ううう
ええええ
おおおおお
# 全体をjoinして終わり
$ $("$(echo あいうえお)".ToCharArray()|% -begin{$NR=0} {"$_"*++$NR}) -join ''
あいいうううええええおおおおお

最後の -join をパイプ越しにできたらいいんですが・・・

問2

11円のうまい棒、21円のチロルチョコ、54円のよっちゃんイカを223円分買うとき、それぞれ何個買うことができるかを求める問題。ただしそれぞれ最低1つは買っていること
さんすう

$ echo {1..10}_{1..10}_{1..10}|fmt -1|awk -F_ '$1*11+$2*21+54*$3==223{print "うまい棒:",$1,"チロルチョコ:", $2, "よっちゃんイカ:",$3}'
うまい棒: 2 チロルチョコ: 7 よっちゃんイカ: 1

厳密には範囲が足りてないんですがこれでも正解が出たのでこれで。こういうのは計算機で全探索したほうが楽ですね!もちろん脳みそでできることも大切だとは思います

$ 1..22 |% {$x=$_; $(1..12|%{echo $x-$_})}|%{$x=$_; $(1..10|%{echo $x-$_})}|? {$x=$($_ -split '-'); [int]$x[0]*11+[int]$x[1]*21+[int]$x[2]*54 -e
q 223}
2-7-1

めちゃめちゃになってしまった…。 bash みたいなブレース展開で組み合わせを作れないのがつらい。1..22 |% {$x=$_; $(1..12|%{echo $x-$_})}|%{$x=$_; $(1..10|%{echo $x-$_})} までで 1-1-1 のような組み合わせを 22-12-10 まで作っています

# 組み合わせ生成
$ $ 1..22 |% {$x=$_; $(1..12|%{echo $x-$_})}|%{$x=$_; $(1..10|%{echo $x-$_})}
1-1-1
1-1-2
...
22-12-10
# ? => Where-Object へのエイリアス。評価式がTrueになるものだけ通過させる
# $xに - でsplitした配列をぶち込む
# intへキャストして計算。223と等しいものだけ通過させる
$ 1..22 |% {$x=$_; $(1..12|%{echo $x-$_})}|%{$x=$_; $(1..10|%{echo $x-$_})}|? {$x=$($_ -split '-'); [int]$x[0]*11+[int]$x[1]*21+[int]$x[2]*54 -e
q 223}
2-7-1

型を指定してsplit出来たりしたらいいんですがね~。思いつくのは $x=$($_ -split '-' | %{[int]$_}) といった感じでしょうか

問3

IPアドレスを2進数32桁に変換する問題。おもしろそう

$ echo 192.168.10.55 | csharp -e "string.Join(\".\", Console.ReadLine().Split('.').Select(int.Parse).Select(x=>Convert.ToString(x, 2).PadLeft(8, '0')))"
"11000000.10101000.00001010.00110111"

mono-csharp-shell がシェル芸botにも入ったので、使ってみました。えっ!?面倒だったからだろって?違いますよぉやだなぁ
ほぼC#の解説になっちゃいますが一応

string.Join(".",       // 後ろのCollectionを.でJoin
    Console.ReadLine() // 一行読み込み string
    .Split('.')        // .でSplit
    .Select(int.Parse) // int型へ
    .Select(
        x=>Convert.ToString(x, 2) // 基数を指定してstringへ
           .PadLeft(8, '0')       // 8桁で0埋め
    )
)

IPAddress という classParse() 、それから IPAddress には GetAddressBytes() があるので、もう少し短く書けるかもしれません。しかし…シェル芸とは?となってしまいますね

$ $("192.168.10.55" -split '\.'|% {[Convert]::ToString([int]$_, 2).PadLeft(8,'0')}) -join '.'
11000000.10101000.00001010.00110111

割と簡単でした。解説します

# "192.168.10.55" を . でsplit
$ "192.168.10.55" -split '\.'
192
168
10
55
# [Convert]::ToString()で基数を指定して文字列にする
$ "192.168.10.55" -split '\.'|% {[Convert]::ToString([int]$_, 2) 
11000000
10101000
1010
110111
# 幅があってないので PadLeftで0埋め
$ "192.168.10.55" -split '\.'|% {[Convert]::ToString([int]$_, 2).PadLeft(8,'0')}
11000000
10101000
00001010
00110111
# .でjoinする
$ $("192.168.10.55" -split '\.'|% {[Convert]::ToString([int]$_, 2).PadLeft(8,'0')}) -join '.'
11000000.10101000.00001010.00110111

復元もやってみます。

$ echo 11000000.10101000.00001010.00110111|csharp -e "string.Join(\".\",Console.ReadLine().Split('.').Select(x=>Convert.ToInt32(x,2)))"
192.168.10.55
$("11000000.10101000.00001010.00110111" -split '\.'|% {[Convert]::ToInt32($_, 2)}) -join '.'
192.168.10.55

Convert.ToInt32() で基数を指定して int にできます

問4

双子素数を出力して下さい

$ seq 100 | factor | awk 'BEGIN{s=2}NF==2{print s,$2; s=$2}'|awk '$2-$1==2'
3 5
5 7
11 13
17 19
29 31
41 43
59 61
71 73

factor素数をえらび、前の素数とのペアになるようなリストを作ります。最後の awk で差分が2のものだけ取り出します

$ seq 100 | factor | awk 'BEGIN{s=2}NF==2{print s,$2; s=$2}'                  
2 2
2 3
3 5
5 7
7 11
...
$ 1..100|factor|? {$_.split(' ').Length -eq 2}|%{$_.split(' ')[1]}|% -begin{$s=2} {"$s $_";$s=$_}|? {$x=$($_ -split ' '|%{[int]$_}); $x[1]-$x[0]
 -eq 2}
3 5
5 7
11 13
17 19
29 31
41 43
59 61
71 73

できるだけpowershellの機能だけでやりたいな~と思うんですが、 factor だけは使おうかな~という感じです。 factorbusyboxでインストールできます

問5

1から9の整数の組み合わせで、足して10になり、互いに数字が異なる組み合わせを全部列挙する問題。0-1ナップザックっぽい

$ seq 4321 |grep -v 0 | grep -Ev '(.).*\1'|awk -F "" '$1+$2+$3+$4==10'
19
28
37
46
64
73
82
91
127
136
145

同行に同じ文字列内に同じ文字が含まれているかどうかの正規表現が思いつきませんでした・・・正規表現むずい・・・

$ 1..4321|? {-not "$_".Contains("0")}|?{$($_ -split ''|measure -sum).Sum -eq 10}|? {-not ($_ -match '(.).*\1')}
19
28
37
46
64
73
82
91
127
136
145

measure は 要素の個数、合計、平均なんかを計算してくれます。

$ 1..30|measure -sum -ave -max -min     

Count    : 30
Average  : 15.5
Sum      : 465
Maximum  : 30
Minimum  : 1

このうちの Sum にアクセスしたい場合は (コマンド).Sum とします。今回は、桁和が10になるものだけを ? でフィルターし、そこからさっきと同じ正規表現でフィルターしました。

問6

nums ファイルから、問5で求めたパターンを探し、それが何行目にあるかを出力する問題。むずSO

$ seq 4321 |grep -v 0 | grep -Ev '(.).*\1'|awk -F "" '$1+$2+$3+$4==10'|while read L; do grep $L nums -n 2>/dev/null | sed "s/:/行目  ${L}がありました  /g"; done
4行目  145がありました  124214535
1行目  235がありました  124123541
5行目  325がありました  433251244
3行目  352がありました  352381324
3行目  523がありました  352381324
1行目  541がありました  124123541
3行目  1324がありました  352381324
2行目  3214がありました  321412412
1行目  4123がありました  124123541

while read L; do ~ done の力業です。各パターンについて、毎回 grep し、マッチすればその行を出力しています。出力を丁寧にしないのであれば、 xargsgrep になげるだけでいいと思います

$ 1..4321|? {-not "$_".Contains("0")}|?{$($_ -split ''|measure -sum).Sum -eq 10}|? {-not ($_ -match '(.).*\1')}|%{sls $_ ./nums}|%{"$($_.LineNum
ber): $($_.Matches.Value)"}     
4: 145
1: 235
5: 325
3: 352
3: 523
1: 541
3: 1324
2: 3214
1: 4123

slsSelect-String へのエイリアスです。 使い方は grep と大体同じ感じですが、マッチした行を返すのではなく、 MatchInfo オブジェクトを返します

$ $("ABCDEFG"|sls A).GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    MatchInfo                                System.Object

MatchInfo オブジェクトは以下のようなメソッドやプロパティを持ちます

$ $("ABCDEFG"|sls A).       
Context       Line          Path          Equals        GetTypeCode   ToByte        ToDecimal     ToInt32       ToSingle      ToUInt16
Filename      LineNumber    Pattern       GetHashCode   RelativePath  ToChar        ToDouble      ToInt64       ToString      ToUInt32
IgnoreCase    Matches       CompareTo     GetType       ToBoolean     ToDateTime    ToInt16       ToSByte       ToType        ToUInt64

ここからマッチした行 LineNumber とマッチした値 Value を取り出して、整形して終わりです

問7

100x100の市松模様画像を作る問題。

yes $(python3 -c "print('01'*50); print('10'*50)")|head -n50|tr ' ' \\n|sed '1iP1 100 100'|convert - ./a.bmp

img

yes01010101....1 1010101...0という無限の出力を作り、必要な数だけ head で取り出します。必要な情報を1行目に sed で挿入して convert に放り込むだけですね。やったぁ

$ 0..99 | %{$x=$_; $(0..99|%{"$x $_"})}|% -begin{$bmp=$(New-Object System.Drawing.Bitmap(100,100))} {$z=$($_ -split ' '; ); $bmp.SetPixel($z[1],
 $z[0],  ([System.Drawing.Color]::White, [System.Drawing.Color]::Black)[($z[0]%2+$z[1]%2)%2])} -end {$bmp.Save("./out.bmp")}

img

こっちは Bitmap オブジェクトにピクセルをどんどん割り当てているだけですね

問8

上で作った画像に文字を仕込み、別の画像を作り、さらにそこから文字を取り出す問題

$ xxd -ps a.bmp|tr -d \\n|sed -E 's/.{8}$/64617465/'|xxd -ps -r > b.bmp

img

$ xxd ./b.bmp | tail
00007520: ff00 0000 ffff ff00 0000 ffff ff00 0000  ................
00007530: ffff ff00 0000 ffff ff00 0000 ffff ff00  ................
00007540: 0000 ffff ff00 0000 ffff ff00 0000 ffff  ................
00007550: ff00 0000 ffff ff00 0000 ffff ff00 0000  ................
00007560: ffff ff00 0000 ffff ff00 0000 ffff ff00  ................
00007570: 0000 ffff ff00 0000 ffff ff00 0000 ffff  ................
00007580: ff00 0000 ffff ff00 0000 ffff ff00 0000  ................
00007590: ffff ff00 0000 ffff ff00 0000 ffff ff00  ................
000075a0: 0000 ffff ff00 0000 ffff ff00 0000 ffff  ................
000075b0: ff00 0000 ffff 6461 7465                 ......date

date を仕込みました

問9

さらにこの画像をpngに変換し、復元、文字を取り出す問題

convert の無圧縮オプションが必要らしい

LT大会

今回もLTさせていただきました。聴いてくださった方ありがとうございました!!

www.slideshare.net

記号プログラミングが得意なPowerShellへ入門してみました。今回もこの記事を書くのと同時にPowerShellで問題を解いてみましたが、便利なメソッドやらコマンドレットがあって意外と楽だなぁと思いました。

それと他の方のLTも聴いてましたがめちゃヤバいですね・・・どうなってるんだ・・・

おわり

今回もとてもつかれました。参加された皆様、企画、運営の皆様ありがとうございました!

owari kan -a xztaityozx  -g   

| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
|        終        |
|    制作・著作    |
|   ̄ ̄ ̄ ̄ ̄ ̄ ̄  |
|    xztaityozx    |
|_________|
    ∧∧ ||     
   ( ゚д゚)||    
    /   づΦ