2016年12月19日月曜日

最近見た不思議なシェルスクリプトを直してみた

この記事は Shell Script Advent Calendar 2016 19日目の記事です。
18日目の記事はryuichiuedaさんの SHELQ: 怪しいシェル芸キュレーションサイト でした。

他の様々なプログラミング言語において良い書き方と悪い書き方があるように、シェルスクリプトにも良い書き方と悪い書き方があります。しかし特にシェルスクリプトの場合は不慣れな人がかなり悪い書き方をしている様です。そこで、幾つかの例を見ながら良い書き方と悪い書き方を比べてみます。

ディレクトリについて再帰的に処理をする

悪い例

$ ls -R | awk '{がんばる}' | 処理

"ls -R" を使うと再帰的にファイルをリストアップすることができます。ということはこの情報を使えば階層の深いファイルについても処理をすることができますね。やった!

良い例

$ find . -print0 | xargs -0 処理 find . -exec 処理 + (あるいは\;)

良く考えてみて下さい。ただ再帰的に処理をするためだけにわざわざ気合を入れてawkでパースする必要があるなら誰かが便利な道具を作っているはずです。こういう場合はfindを使います。findを使えばファイル名や種類、更新日時など様々な条件でファイルを列挙することができます。ただし、xargsに渡す際に空白入りファイル名があると変な場所で区切れるので所定のオプションを付けてヌル文字区切りにすると良いです。

特定のコマンドを実行しているプロセスを列挙する

悪い例

\$ ps ax | grep 実行ファイル名 | grep -v grep | awk '\$0=\$1'
\$ ps ax | grep [実]行ファイル名 | awk '\$0=\$1'

多分何も知らないと最初に挙げている、ps axの結果を実行ファイル名でgrepしてからgrepを弾くようになると思います。しかし実は、一文字に[]を付けても正規表現として内容が変わらないことを利用して自分自身を弾くこともできます。

良い例

$ pgrep 実行ファイル名

プロセス番号以外も取得したいのであれば二つめに挙げた方法を使うのが良いと思いますが、欲しいのがプロセス番号であればpgrepという正にこのために使うものがあります。 (実は少し結果が変わる場合もありますが多分pgrepの挙動が好ましい場合がほとんどだと思います。) さらにプロセスをkillしたいのであればpkillもあるので便利です。環境によっては無いかもしれません。今時のLinux以外の環境のことは知りません。

番外編 (timeout)

ところで僕がこの処理を見たスクリプトは定期的に走らせて前回と同じプロセスが走りっぱなしであれば強制終了するというものでした。これでもいいんですが、単にタイムアウトの処理をやれば済むのであればGNU coreutilsにはtimeoutが入っています。簡単ですね。

時系列ファイル名の最新版取得

ファイル名が「%y%m%d%H%M%S.log」とかで時系列につけられているファイル群のうち、一番新しいのを取得する方法です。ログとか取ってると良くありますね。

悪い例

$ exprを使ってがんばる

もうどうやるのか知りたくもない感じです。80行かけると書けるらしいです。ソートでも実装するんでしょうか。

良い例

$ sort -nr | head -n 1

ソートするだけです。日時は単に数値としてソートしても結果は同じですよね。GNU sortは優秀で、バージョンなど色々な方法でソートできるらしいです。実は数値と辞書順しか使ったことはないですが。

20日目はkunst1080さんの記事です。

2016年5月27日金曜日

第22回ゴールデンウィークの存在疑惑シェル芸勉強会 第1問

相変わらずシェル芸勉強会には参加できなかったので後日解いてみました。とりあえず第一問です。問題と解答はここにあります。

第1問

最初の解答

だいぶ狂った解答です。最初に全体の行数をwcで数えてそこからAWKでこねこねしてます。

場合分けのない解法

ここ
データ数が偶数と奇数の時で場合分けが必要で面倒くさいです。(場合分けのない方法絶賛募集中。)
と書いてあったので場合分けをしない解答も考えました。

気分がわかるようにAWKで一気に書かれている部分をほぐして説明します。 入力が

3.4
13
42
-4
-5
の時は sortして
-5
-4
3.4
13
42
反転したものと横に並べて
-5 42
-4 13
3.4 3.4
13 -4
42 -5
差の2乗と平均を出して
2209 18.5
289 4.5
0 3.4
289 4.5
2209 18.5
sortしたときの第二カラムの値が中央値です。
0 3.4
289 4.5
289 4.5
2209 18.5
2209 18.5

反転したものと横に並べる周りが汚い感じになっちゃってます。もう少し何とかならないものでしょうか(tukubaiとか?)

2016年2月21日日曜日

安心してください。AWKですから。【トポロジカルソート篇】

定期的に思うことですが、アルゴリズム力がないので諸々のアルゴリズムの復習をします。 最も書き慣れている言語のうちのひとつで、結構書きやすいと思うのでAWKで書くことにします。(布教の意味も込めて)

今回はトポロジカルソートを書こうと思います。トポロジカルソートと言えばGNU coreutilsでtsortとして実装されていることがシェルっぽい人のなかでは有名だと思います。 そうは言ってもシェルスクリプトの中でどうやったら上手く使えるのかはわかりません。 トポロジカルソートは入力に対して答えが一意に定まらないのでtsortとは結果がかわるかもしれません。

アルゴリズムの説明

細かい説明はWikipediaを参照してください。トポロジカルソートでは入力として有向グラフが与えられます。出力は有向グラフのnodeを、edgeの向きが逆転しないようにした順序になります。トポロジカルソートの解は一般に一意に定まりません。 有向グラフがリソースの依存関係を表わしていると考えるとき、トポロジカルソートの出力は依存関係を満たすようなbuildなどの順序になります。有向グラフがpartial orderを表わしていると考えるとき、total orderに拡張していると見ることもできます。

プログラムの仕様

AWKでは(gawkでは独自拡張で実装されてますが)多次元配列がなく、一次元の連想配列しかありません。 そのためグラフの辺の情報を


edge[source] = target0 target1 target2 ... targetn
のように空白区切りで実装しています。 sourceに向う辺を検索する部分は、各nodeについてsourceに向かう辺があるかどうかを線形に探索しています。 辺を削除した結果どこにも向う辺がないnodeについては、空白のtargetを代入することで実装しています。

入力はtsortとほぼ同じですが、各行ごとにsourceとtargetのnodeを書く必要があります。出力はtsortと同じ仕様です。

2016年1月1日金曜日

魚へんマッチングコーディング書き初め

先日のシェル芸勉強会で魚へんの漢字を探す問題が出題されました。本当はUnicodeで魚へんの漢字が連続して出てくることを利用して解く問題ですが、それだとなんか面白くないので画像マッチングで解くことにしました。

やったこと

やったことは非常にシンプルで、同一のフォントなら文字が変わっても大体魚へんの部分は一致するだろうという雑な推定を使いました。従って"魚"や"鯊"などの部首の区分では魚かもしれないが見た目が違うものについては対応する気がありません。魚の大きさが全く違うようなデータが来ても全然ダメです。

まあ普通にC++とかで実装すればいいんですが、芸がない (シェル芸という意味ではないですよ) のでC++でCLIのツールの様なものを実装してシェルで動かしたことにしました。

作ったもの

https://github.com/MasWag/image_utils にあります。Arch Linuxで動かしました。おまけで画面をキャプチャするツールも作りました。これはX11じゃないと動かないのでLinuxじゃないと動かないかもしれません。画面をキャプチャしてパターンとマッチした部分をクリックみたいなものを書けて便利です。

動かしかた

画像データは http://www.unicode.org/ から持ってきました。大量に持ってくると怒られそうなのでほどほどにしてください。ローカルに置くと良いと思います。OpenCVではgifは扱えないのでImageMagickなどで事前にpngに変換して下さい。魚へんの部分もImageMagickでcropとかして適当に作ってください。

./imread tachiuo.png gomi.png maguro.png | ./matchTemplate sakanahen.png

を実行すると

0.868365 6 23 0.473221 16 23 0.682904 6 23

みたいな出力がされます。第一フィールドが大きい方が正解っぽいデータです。第二、第三フィールドがマッチした部分の中央の座標です。第一フィールドを見てもわかりますが、今回は6が左側の中心なのでへんらしい場所にマッチしていることがわかります。多分Unicode全部の画像を持ってきてある程度以上マッチしたデータを全部集めると魚へんが残りますが、流石にそんなことはやってません。