生涯未熟

生涯未熟

プログラミングをちょこちょこと。

os.Stat, os.Lstatのシンボリックリンク指定時の違い

いつまで経っても覚えられないので自戒のために書く。

os.Stat

指定されたファイルパスの情報(os.FileInfo)を返す。
シンボリックリンクを指定した場合には、リンクを辿った先の情報を返す。

os.Lstat

指定されたファイルパスの情報(os.FileInfo)を返す。 シンボリックリンクを指定された場合には、リンクを辿らずシンボリックリンク自体の情報を返す。

注意

os.FileInfoのNameはStat, Lstatのどちらも指定したシンボリックリンクの名前自体になる。

echoのmiddlewareで一度読んだRequestBodyをもう一度読む

Gopherの皆さんはecho使ってますでしょうか?僕は薄くて好きなのでよく使ってます。

さて、echoにはmiddlewareという機能がございます。
Middleware | Echo - High performance, minimalist Go web framework

ハンドラの実行前に処理差し込めるやつですが、このmiddlewareとして以下のような処理を差し込むとします。

func CheckJSON(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        jsonBlob, err := ioutil.ReadAll(c.Request().Body)
        if err != nil {
            return err
        }
        s := string(jsonBlob)
        if !validator.IsJSON(s) {
            return c.NoContent(http.StatusBadRequest)
        }

        return next(c)
    }
}

飛んできたリクエストがJSONかどうか?というのをチェックするmiddlewareになります。
さて、このmiddlewareを実行した後にハンドラでこのような処理を実行するとしましょう。

func Hoge(c echo.Context) error {
    var jsonBody map[string]interface{}
    jsonBlob, err := ioutil.ReadAll(c.Request().Body)
    if err != nil {
        return err
    }
    err := json.Unmarshal(jsonBlob, &jsonBody)
    if err != nil {
        return errors.New("failed unmarshal json")
    }

    return c.NoContent(200)
}

またもやリクエストから読み込んでる処理ですね。
さて、これを実行すると failed unmarshal json のエラーになります。
これは ioutil.ReadAll がc.Request().Bodyを全て読み込んでいるため、次回以降読み込もうとするとnilが返ってくるためです。

で、ここからどうやって2回目の読み込みを実現するかですが、答えは簡単で「Bodyの内容を上書く」という解決方法があります。

func CheckJSON(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        jsonBlob, err := ioutil.ReadAll(c.Request().Body)
        if err != nil {
            return err
        }
        s := string(jsonBlob)
        if !validator.IsJSON(s) {
            return c.NoContent(http.StatusBadRequest)
        }

        c.Request().Body = ioutil.NopCloser(bytes.NewBuffer(jsonBlob))

        return next(c)
    }
}

こうすることで2回目にキチンと ioutil.ReadAll を実行しても内容を読み込んでくれます。

刺し身たんぽぽについて

刺し身たんぽぽについて調べてみました。

f:id:syossan:20190106180834p:plain

刺し身たんぽぽとは一体何なのか?

刺身の盛り合わせに添えられている食菊(食用菊)の通称。花の大きさからタンポポと誤認されやすい。菊には殺菌作用があり、かつ見た目の彩りや香りも添える。さらに刺身のツマとして食べられる。近年では形骸化しつつある。 - weblio辞書より引用 -

なるほど、実際にはたんぽぽでは無いのですね。勉強になります。
あと、食べれるものとは知りませんでした。むしろ、あんな花花しいものを食べる人はいるのだろうか?

いつから刺し身たんぽぽは誕生したのか?

奈良時代に、日本で現在でも食用菊として栽培されている「延命楽(もってのほか・カキノモト)」が中国から伝来した[2]。 - wikipediaより引用 -

そんな1000年以上前から刺し身たんぽぽはあったのですね・・・しゅごい。

食用としては、江戸時代から民間で食されるようになったとされており[4]、1695年に記された『本朝食鑑』に「甘菊」の記述が見られる[5]。 - wikipediaより引用 -

実際に食されるようになったのは結構最近なのですね。
刺し身のツマとして用いられるようになった経緯についても深掘りしたかったのですが、検索しても見つからず。インターネットの敗北です。

刺し身たんぽぽの効用とは?

刺し身にたんぽぽが入っている理由としては「殺菌作用・彩り」の2点のようですね。
このたんぽぽ自体には発ガン効果の抑制・コレステロールの低下・中性脂肪を低下させる効果があるそうです。めちゃくちゃ有能やんけ。

刺し身たんぽぽの危険性

ただ、こんな刺し身たんぽぽですがキク科のアレルギーを持つ方にとっては食すのに危険性が伴います。

togetter.com

食べる時には自分がアレルギーを持っていないか気を付けましょう。
また、刺し身たんぽぽという単語を真に受けて、その辺に生えてるたんぽぽを生で食べないようにしましょう。普通に汚いですからね。

刺し身たんぽぽについて完全に理解した

年始から刺し身たんぽぽの話題で溢れていたので、完全に理解するために色々と調べてみましたがどうだったでしょうか?
こういう単語はしっかり調べた上で、きちんと使える大人になりましょうね!

treeコマンド 〜Rオプションの謎〜

treeコマンド、皆さん知ってますか?
そうです、あのファイルやらディレクトリやらを良い感じに木構造で出力してくれるニクい奴です。

f:id:syossan:20190103044420p:plain

こんな感じ

さて、そんなtreeコマンドには多くのオプションがあります。
よく使われるのは -L だったり、 -a とかですかね?

そんな中、 -R オプションというものが存在するのをご存知でしょうか?

Rオプションとは何なのか?

まずは、 --help オプションに書かれている内容を見てみましょう。

-R            Rerun tree when max dir level reached.

直訳すると 最大ディレクトリレベルに達したらツリーを再実行します。 ってとこですかね。
あー、再帰的にツリーを辿るのねと試しに以下のようなコマンドを実行してみました。

$ tree -L 1 -R -o result

これで1階層ごとにtreeの結果が出力されるじゃね?と思ってみました。(あまりやっても意味ないですが・・・
で、やってみた結果、ワーキングディレクトリの下にしかresultが吐かれませんでした・・・

さてはて、予想が外れたのですが冷静に次はmanコマンドを参照してみます。

-R     Recursively cross down the tree each level directories (see -L option), and at each of them execute tree again adding `-o 00Tree.html' as a new option.

これを直訳すると

再帰的に各レベルのディレクトリにツリーをたどります(-Lを参照)
そして、それらのそれぞれで、新しいオプションとして '-o 00Tree.html'を追加して、それぞれtreeを実行します。

オッ、 --help の時と違って新たな -o 00Tree.html という概念が出てきましたね。
ただ、先の実行結果を見渡してみても 00Tree.html とやらが吐き出されている様子は無い模様。
ますます頭にはてなマークが生えてきました。

treeコマンドのソースコードを読む

このままでは埒が明かないので、treeコマンドのソースコードを読んじゃいましょう。
C言語で書かれているので、そこまで難しくはないはずです。

ソースコードは以下のリンク先からよしなに取得してきてください。
The Tree Command for Linux Homepage

さて、それでは読んでいきたいと思います。

まず、何はともあれ -R オプションを追っかけましょう。

case 'R':
  Rflag = TRUE;
  break;

-R オプションはこのようにコード内では Rflag という変数に置き換えられます。
これを追いかけようと、tree.cの中を見てみたのですが、まさかの以下のコードしか参照しているところがありませんでした。

if (Rflag && (Level == -1))
  Rflag = FALSE;

んん?意味自体は分かりますが、これが再帰処理に繋がる気配は無さそうですね・・・
一応他のコードも読んでみたのですが、これまたまさかの html.c というファイル内に以下の意味ありげなコードが書かれていました。

/* This is really hackish and should be done over. */
if (Rflag && (lev == Level) && (*dir)->isdir) {
  if (nolinks) fprintf(outfile,"%s",(*dir)->name);
  else {
    fprintf(outfile,"<a href=\"%s",host);
    url_encode(outfile,d+1);
    putc('/',outfile);
    url_encode(outfile,(*dir)->name);
    fprintf(outfile,"/00Tree.html\">");
    html_encode(outfile,(*dir)->name);
    fprintf(outfile,"</a><br>\n");
  }

  hdir = gnu_getcwd();
  if (sizeof(char) * (strlen(hdir)+strlen(d)+strlen((*dir)->name)+2) > pathsize)
    path = xrealloc(path, pathsize = sizeof(char) * (strlen(hdir)+strlen(d)+strlen((*dir)->name) + 1024));

  sprintf(path,"%s%s/%s",hdir,d+1,(*dir)->name);
  fprintf(stderr,"Entering directory %s\n",path);

  hcmd = xmalloc(sizeof(char) * (49 + strlen(host) + strlen(d) + strlen((*dir)->name)) + 10 + (2*strlen(path)));
  sprintf(hcmd,"tree -n -H \"%s%s/%s\" -L %d -R -o \"%s/00Tree.html\" \"%s\"\n", host,d+1,(*dir)->name,Level+1,path,path);
  system(hcmd);
  free(hdir);
  free(hcmd);
}

うーーーーーん、This is really hackish and should be done over.というコメントから察するに、なんか難易度高そうっすね・・・
一応この処理は -H オプションを付けて実行した際のHTMLを構築する処理の一部になります。

HTML構築のためにディレクトリを -L で指定した深さまで辿っている途中に、この処理が出てくるのですが、ざっくり読み解いてみると、どうやらRオプションがあり、かつLオプションで指定した深さのディレクトリの場合にHTMLを出力しつつ、再帰的に tree コマンドを実行していますね。
manの説明にあった、 00Tree.html の存在も伺えます。どうやら再帰的に実行する際に、各ディレクトリに 00Tree.html を生成しているようです。

というわけで、なんとなく分かったこととして -R オプションは -H オプションと共に使わないと意味が無さそうってことですね。
では実験してみましょう。

実験

それでは -H オプションを付加してやってみましょう。

$ tree -L 1 -HR .

<!DOCTYPE html>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <meta name="Author" content="Made by 'tree'">
 <meta name="GENERATOR" content="$Version: $ tree v1.8.0 (c) 1996 - 2018 by Steve Baker, Thomas Moore, Francesc Rocher, Florian Sesser, Kyosuke Tokoro $">
 <title>Directory Tree</title>
 <style type="text/css">
  <!--
  BODY { font-family : ariel, monospace, sans-serif; }
  P { font-weight: normal; font-family : ariel, monospace, sans-serif; color: black; background-color: transparent;}
  B { font-weight: normal; color: black; background-color: transparent;}
  A:visited { font-weight : normal; text-decoration : none; background-color : transparent; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }
  A:link    { font-weight : normal; text-decoration : none; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }
  A:hover   { color : #000000; font-weight : normal; text-decoration : underline; background-color : yellow; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }
  A:active  { color : #000000; font-weight: normal; background-color : transparent; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }
  .VERSION { font-size: small; font-family : arial, sans-serif; }
  .NORM  { color: black;  background-color: transparent;}
  .FIFO  { color: purple; background-color: transparent;}
  .CHAR  { color: yellow; background-color: transparent;}
  .DIR   { color: blue;   background-color: transparent;}
  .BLOCK { color: yellow; background-color: transparent;}
  .LINK  { color: aqua;   background-color: transparent;}
  .SOCK  { color: fuchsia;background-color: transparent;}
  .EXEC  { color: green;  background-color: transparent;}
  -->
 </style>
</head>
<body>
    <h1>Directory Tree</h1><p>
    <a href=".">.</a><br>
    ├── <a href="./LICENSE">LICENSE</a><br>
    ├── <a href="./Makefile">Makefile</a><br>
    ├── <a href="./README.md">README.md</a><br>
    ├── <a href="./cmd/00Tree.html">cmd</a><br>
Entering directory /Users/syossan27/go/src/go-tree/cmd
Entering directory /Users/syossan27/go/src/go-tree/cmd/go-tree
<br>
    ├── <a href="./dist/00Tree.html">dist</a><br>
Entering directory /Users/syossan27/go/src/go-tree/dist
<br>
    ├── <a href="./go.mod">go.mod</a><br>
    ├── <a href="./go.sum">go.sum</a><br>
    ├── <a href="./tree.go">tree.go</a><br>
    ├── <a href="./tree.html">tree.html</a><br>
    ├── <a href="./tree_windows.go">tree_windows.go</a><br>
    └── <a href="./validate.go">validate.go</a><br>
    <br><br>
    </p>
    <p>

2 directories, 9 files
    <br><br>
    </p>
    <hr>
    <p class="VERSION">
         tree v1.8.0 © 1996 - 2018 by Steve Baker and Thomas Moore <br>
         HTML output hacked and copyleft © 1998 by Francesc Rocher <br>
         JSON output hacked and copyleft © 2014 by Florian Sesser <br>
         Charsets / OS/2 support © 2001 by Kyosuke Tokoro
    </p>
</body>
</html>

おー、HTMLは無事出力されましたね。
この状態で、 00Tree.html とやらは各ディレクトリに出力されているのか確認します。

$ tree
.
├── LICENSE
├── Makefile
├── README.md
├── cmd
│   ├── 00Tree.html
│   └── go-tree
│       ├── 00Tree.html
│       └── main.go
├── dist
│   ├── 00Tree.html
│   ├── go_tree_darwin_386
│   ├── go_tree_darwin_amd64
│   ├── go_tree_freebsd_386
│   ├── go_tree_freebsd_amd64
│   ├── go_tree_freebsd_arm
│   ├── go_tree_linux_386
│   ├── go_tree_linux_amd64
│   ├── go_tree_linux_arm
│   ├── go_tree_netbsd_386
│   ├── go_tree_netbsd_amd64
│   ├── go_tree_netbsd_arm
│   ├── go_tree_openbsd_386
│   ├── go_tree_openbsd_amd64
│   ├── go_tree_windows_386.exe
│   └── go_tree_windows_amd64.exe
├── go.mod
├── go.sum
├── tree.go
├── tree.html
├── tree_windows.go
└── validate.go

オッ、たしかに出てますね。

で、出力されたHTMLを見てみると以下のような感じになります。

f:id:syossan:20190103053127p:plain

ほほー?一応cmdディレクトリのリンクを踏んでみましょうか。

f:id:syossan:20190103053158p:plain

なるほど、リンク先もtree構造のHTMLになってますね。
これが -R オプション無しだと、リンク先を踏んだ場合にはこんな感じになります。

f:id:syossan:20190103053251p:plain

普通にブラウザでローカルディレクトリを覗いた時の画面ですね。
という形の結果になりました。

まとめ

結局のところ -R オプションってなんぞ?というところなのですが、これはtreeコマンドの開発者が単に内部的に00Tree.htmlを吐く機構が欲しかったから作ったのでは・・・?という感想があります。
CHANGELOGとかも遡って見たり、インターネット上を彷徨ったりしたのですが特に有力な情報は見つからず・・・

あと、ちょっと気になるところとして 00Tree.html の親ディレクトリへのリンク先を押しても戻ることが出来ず、リンク先の出力ロジックがなんか間違えてるんじゃないかと。
こういった時のバグの報告とかどうやればいいのかちょっと謎なので、treeコマンドに詳しい方、情報お待ちしております。

2018年の棚卸し

2018年もそろそろ終わりですね。

恒例の本年の棚卸しをやっていきたいと思います。

1月

syossan.hateblo.jp

今年の抱負は「つよく いきる」でした。
つよく いきました、よかったですね。

syossan.hateblo.jp

homebrewに自作ツール登録しようとして失敗したりしました。
来年は登録基準を満たすレベルのリポジトリを生成したいですね。

2月

syossan.hateblo.jp

TebataというLinuxシグナルをハンドリングするGoライブラリ作ったりしました。
ちょこちょこと使って頂いてるみたいで、

github.com

そこそこ大きいライブラリでも使って頂いたり、

github.com

何故かワームプログラムに使われたりしてました。
悪いことはやめようね!

3月

syossan.hateblo.jp

なんかバズりました。
けど結局Twitterをまだやってます。付き合い方は変わったと思うので、この時の経験は活きたのかな?と思います。
一旦距離を置くというのは大事ですね。

4月

syossan.hateblo.jp

Goの勉強会を開催し始めました。
僕の中でgolang.tokyoや、GoConでの登壇って皆様発表内容が高レベルでハードル高いなーと思っていたので、なんかゆるっと登壇できる勉強会を作りたかったのです。
来年も細々と続けていきたいです。

5月

syossan.hateblo.jp

クソ雑魚ナメクジエンジニアはこんなくらいのスカウトが来るよって事例を書いてみたりしました。
直近の転職ドラフト結果もまた書きたいですね。

syossan.hateblo.jp

主催の勉強会でGoの並行処理についての発表しました。
英語苦手なりに原書を頑張って読んだら、この5か月後に和訳版が発売されました。泣きたい。
というのは冗談で、めちゃくちゃ良い本なのでGopherは是非読みましょう。

6月

syossan.hateblo.jp

Cassandra周りをガリガリやってた時に、あまり日本語情報が無い現象にぶち当たったりしました。

syossan.hateblo.jp

Kafkaもガリガリとやってたので、ライブラリの挙動にハマったりしましたねぇ。
Kafkaはもっと極めていきたい。

7月

syossan.hateblo.jp

ランサーズさんの開発ランチにお邪魔したりもしました。

syossan.hateblo.jp

Go猿本読んだりもしました。
来年もGoの良本出ないかな〜。

gounconference.connpass.com

勉強会3回目をやったり。

8月

syossan.hateblo.jp

書籍のレビューをやらせて頂いたりもしました。
貴重な経験ができました。

syossan.hateblo.jp

あと、Sparkが分からなすぎてキレたりしてました。

9月

syossan.hateblo.jp

この頃からElmの教祖に熱心な勧誘を受け、Elm教に入信致しました。
今ではフロントはElm以外で書く気がしません。

syossan.hateblo.jp

buildersconに初参加もしました。
jollyjoester・・・一体何者だったんだ・・・

10月

gounconference.connpass.com

4回目やりました。

そして30になりました。
30代が不安過ぎてつらい。

11月

syossan.hateblo.jp

これ、今年めちゃくちゃ嬉しかったです。
ありがとうJetBrains、来年もいっぱいお布施します。

12月

アドカレ5本仕込みました。
あまりウケがよろしくなかったので、来年はもっとウケるようなネタを書きたいですね。

まとめ

というわけで、30になってしまいました。
こんなクソ雑魚ナメクジエンジニアなのになんの成果もなく30になってしまい、将来が不安です。
とりあえず来年もがむしゃらにエンジニアリングをがんばっていきたいと思います。