List of Cli - text.Baldanders.info
tag:text.Baldanders.info,2020-10-02:/tags
2020-10-02T16:40:54+09:00
帰ってきた「しっぽのさきっちょ」
https://text.baldanders.info/images/avatar.jpg
https://text.baldanders.info/images/avatar.jpg
Go 製のツールとパッケージをまとめてアップデートした
tag:text.Baldanders.info,2020-10-02:/release/2020/10/released-tools-and-packages-by-golang/
2020-10-02T07:40:54+00:00
2021-12-04T02:40:05+00:00
Update gpgpdump, books-data, gnkf, pa-api, aozora-api, openbd-api, gocli, and errs packages
Spiegel
https://baldanders.info/profile/
<p><a href="https://github.com/">GitHub</a> リポジトリにある <a href="https://go.dev/">Go 言語</a>製のツールやパッケージについて <a href="https://github.com/">GitHub</a> Actions で CI (Continuous Integration) を回す目処が立ったので,設定を変更しリリースし直すことにした。
今回リリースしたツール・パッケージのうち主なものは以下の通り。</p>
<ul>
<li><a href="https://github.com/spiegel-im-spiegel/gpgpdump/releases/tag/v0.9.1">Release v0.9.1 · spiegel-im-spiegel/gpgpdump · GitHub</a></li>
<li><a href="https://github.com/spiegel-im-spiegel/books-data/releases/tag/v0.5.8">Release v0.5.8 · spiegel-im-spiegel/books-data · GitHub</a></li>
<li><a href="https://github.com/spiegel-im-spiegel/gnkf/releases/tag/v0.1.3">Release v0.1.3 · spiegel-im-spiegel/gnkf · GitHub</a></li>
<li><a href="https://github.com/spiegel-im-spiegel/pa-api/releases/tag/v0.7.2">Release v0.7.2 · spiegel-im-spiegel/pa-api · GitHub</a></li>
<li><a href="https://github.com/spiegel-im-spiegel/aozora-api/releases/tag/v0.2.6">Release v0.2.6 · spiegel-im-spiegel/aozora-api · GitHub</a></li>
<li><a href="https://github.com/spiegel-im-spiegel/openbd-api/releases/tag/v0.2.6">Release v0.2.6 · spiegel-im-spiegel/openbd-api · GitHub</a></li>
<li><a href="https://github.com/spiegel-im-spiegel/gocli/releases/tag/v0.10.3">Release v0.10.3 · spiegel-im-spiegel/gocli · GitHub</a></li>
<li><a href="https://github.com/spiegel-im-spiegel/errs/releases/tag/v1.0.2">Release v1.0.2 · spiegel-im-spiegel/errs · GitHub</a></li>
</ul>
<p>若干 lint に叱られたり軽微なバグを指摘されたりしたものもあるが,機能上の追加・変更はない。</p>
<p>それじゃあ,次のステージに行きましょう♪</p>
<h2>ブックマーク</h2>
<ul>
<li>
<p><a href="https://text.baldanders.info/release/gpgpdump/">OpenPGP パケットを可視化する gpgpdump</a></p>
</li>
<li>
<p><a href="https://text.baldanders.info/release/books-data/">書籍データ取得ツール books-data</a></p>
</li>
<li>
<p><a href="https://text.baldanders.info/release/gnkf/">GNKF: Network Kanji Filter by Golang</a></p>
</li>
<li>
<p><a href="https://text.baldanders.info/release/pa-api-v5/">Go 言語用 PA-API v5 クライアント・パッケージ</a></p>
</li>
<li>
<p><a href="https://text.baldanders.info/release/aozora-api-package-for-golang/">Go 言語用青空文庫 API クライアント・パッケージ</a></p>
</li>
<li>
<p><a href="https://text.baldanders.info/release/openbd-api-package-for-golang/">Go 言語用 openBD クライアント・パッケージ</a></p>
</li>
<li>
<p><a href="https://text.baldanders.info/release/gocli-package-for-golang/">Go 言語用 CLI プログラミング支援パッケージ</a></p>
</li>
<li>
<p><a href="https://text.baldanders.info/release/errs-package-for-golang/">Go 言語用エラーハンドリング・パッケージ</a></p>
</li>
<li>
<p><a href="https://text.baldanders.info/golang/using-golangci-lint-action/">golangci-lint を GitHub Actions で使う</a></p>
</li>
<li>
<p><a href="https://text.baldanders.info/golang/check-for-vulns-in-golang-dependencies/">Go 依存パッケージの脆弱性検査</a></p>
</li>
<li>
<p><a href="https://text.baldanders.info/golang/cross-compiling-in-github-actions-with-goreleaser/">GitHub Actions でクロス・コンパイル(GoReleaser 編)</a></p>
</li>
<li>
<p><a href="https://text.baldanders.info/remark/2020/10/github-code-scanning-with-golang/">Go のコードでも GitHub Code Scanning が使えるらしい</a></p>
</li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B01MZ8UA8O?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/516hKjO3t-L._SL160_.jpg" width="160" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B01MZ8UA8O?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">射手座☆午後九時Don't be late</a></dt>
<dd>シェリル・ノーム starring May'n (メインアーティスト)</dd>
<dd>FlyingDog 2008-05-08 (Release 2017-01-16)</dd>
<dd>MP3 ダウンロード</dd>
<dd>B01MZ8UA8O (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="4"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="far fa-star"></i></abbr></dd>
</dl>
<p class="description">移動中に聴くとノリノリになれる(笑)</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2020-10-02">2020-10-02</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 射手座☆午後九時 Don't be late -->
Go 言語用 CLI プログラミング支援パッケージ
tag:text.Baldanders.info,2019-09-07:/release/gocli-package-for-golang/
2019-09-07T02:55:58+00:00
2022-04-03T07:00:58+00:00
このパッケージをそのまま使うことは想定しておらず,何らかのアレンジを加えた上で,それぞれの CLI ツール用に組み込むことを念頭に置いている。
Spiegel
https://baldanders.info/profile/
<ul>
<li><a href="https://github.com/goark/gocli">goark/gocli: Minimal Packages for Command-Line Interface</a></li>
</ul>
<p>本パッケージ <a href="https://github.com/goark/gocli" title="goark/gocli: Minimal Packages for Command-Line Interface"><code>gocli</code></a> は <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a> で CLI (Command-Line Interface) を構成する際に必要になるであろう細々とした機能をまとめたライブラリである。
ただし,このパッケージをそのまま使うことは想定しておらず(そのまま使ってもいいけど)何らかのアレンジを加えた上で,それぞれの CLI ツール用に組み込むことを念頭に置いている。</p>
<p>このため <a href="https://github.com/goark/gocli" title="goark/gocli: Minimal Packages for Command-Line Interface"><code>gocli</code></a> では <a href="https://go.dev/">Go</a> コンパイラが提供する標準パッケージ以外の外部パッケージはなるべく使わないようにし,ライセンスも,あらゆる権利を放棄した <a href="http://creativecommons.org/publicdomain/zero/1.0/" title="Creative Commons — CC0 1.0 Universal">CC0</a> を設定している。</p>
<p>なお <a href="https://github.com/goark/gocli" title="goark/gocli: Minimal Packages for Command-Line Interface"><code>gocli</code></a> パッケージは <a href="https://go.dev/">Go</a> 1.13 以上を要求する。
ご注意を。</p>
<p><a href="https://github.com/goark/gocli/actions"><img src="https://github.com/goark/gocli/workflows/vulns/badge.svg" alt="check vulns"></a>
<a href="https://github.com/goark/gocli/actions"><img src="https://github.com/goark/gocli/workflows/lint/badge.svg" alt="lint status"></a>
<a href="https://raw.githubusercontent.com/goark/gocli/master/LICENSE"><img src="https://img.shields.io/badge/license-CC0-blue.svg" alt="GitHub license"></a>
<a href="https://github.com/goark/gocli/releases/latest"><img src="https://img.shields.io/github/release/goark/gocli.svg" alt="GitHub release"></a></p>
<h2>標準入出力と終了コード</h2>
<p><a href="https://github.com/goark/gocli" title="goark/gocli: Minimal Packages for Command-Line Interface"><code>gocli</code></a><code>/rwi</code> パッケージは標準入出力をコンテキスト情報として格納する構造体を提供する。
また <a href="https://github.com/goark/gocli" title="goark/gocli: Minimal Packages for Command-Line Interface"><code>gocli</code></a><code>/exitcode</code> パッケージは CLI 終了時の終了コードを定義する。</p>
<p>両者は以下のように使う。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/goark/gocli/exitcode"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/goark/gocli/rwi"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">run</span><span class="p">(</span><span class="nx">ui</span> <span class="o">*</span><span class="nx">rwi</span><span class="p">.</span><span class="nx">RWI</span><span class="p">)</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">ExitCode</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ui</span><span class="p">.</span><span class="nf">Outputln</span><span class="p">(</span><span class="s">"Hello world"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">Normal</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">run</span><span class="p">(</span><span class="nx">rwi</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithReader</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdin</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithWriter</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithErrorWriter</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="p">)).</span><span class="nf">Exit</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><a href="https://github.com/goark/gocli" title="goark/gocli: Minimal Packages for Command-Line Interface"><code>gocli</code></a><code>/rwi</code> パッケージを使うメリットはテストで発揮される。
たとえば上述の <code>run()</code> 関数をテストするのであれば</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">outBuf</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">outErrBuf</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">code</span> <span class="o">:=</span> <span class="nf">run</span><span class="p">(</span><span class="nx">rwi</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithWriter</span><span class="p">(</span><span class="nx">outBuf</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithErrorWriter</span><span class="p">(</span><span class="nx">outErrBuf</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"><span class="p">))</span>
</span></span></code></pre></div><p>として実行結果を <code>code</code>, <code>outBuf</code> および <code>outErrBuf</code> から取り出し評価することができる。</p>
<h2>SIGNAL をハンドリングする</h2>
<div class="box"><p><strong>【2021-09-19 追記】</strong></p>
<p><a href="https://go.dev/">Go</a> 1.16 から <a href="https://pkg.go.dev/os/signal" title="signal package - os/signal - pkg.go.dev"><code>signal</code></a><code>.NotifyContext()</code> が導入され <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a> パッケージと連携できるようになった。
本節の機能は既に deprecated であり,利用はおすすめしない。</p>
</div>
<p><a href="https://github.com/goark/gocli" title="goark/gocli: Minimal Packages for Command-Line Interface"><code>gocli</code></a><code>/signal</code> パッケージは標準の <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a> パッケージと組み合わせて SIGNAL のハンドリングを行う。
たとえば,こんな感じ</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"context"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/goark/gocli/signal"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">ticker</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">NewTicker</span><span class="p">(</span><span class="mi">1</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span> <span class="c1">// 1 second cycle
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">defer</span> <span class="nx">t</span><span class="p">.</span><span class="nf">Stop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">select</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">now</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">t</span><span class="p">.</span><span class="nx">C</span><span class="p">:</span> <span class="c1">// ticker event
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">now</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">RFC3339</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="o"><-</span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Done</span><span class="p">():</span> <span class="c1">// cancel event from context
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Stop ticker"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Err</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Run</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">errCh</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">error</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nb">close</span><span class="p">(</span><span class="nx">errCh</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">child</span><span class="p">,</span> <span class="nx">cancelChild</span> <span class="o">:=</span> <span class="nx">context</span><span class="p">.</span><span class="nf">WithTimeout</span><span class="p">(</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">signal</span><span class="p">.</span><span class="nf">Context</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Interrupt</span><span class="p">),</span> <span class="c1">// cancel event by SIGNAL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="mi">10</span><span class="o">*</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">,</span> <span class="c1">// timeout after 10 seconds
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nf">cancelChild</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">errCh</span> <span class="o"><-</span> <span class="nf">ticker</span><span class="p">(</span><span class="nx">child</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">errCh</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Done"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">Run</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
<p>このコードでは <code>signal.Context()</code> 関数で指定した SIGNAL 用の <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a><code>.Context</code> インスタンスを生成している。
SIGNAL または親 <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a><code>.Context</code> インスタンスによるキャンセルイベントを受信した場合は,子 <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a><code>.Context</code> インスタンスにキャンセルが伝搬する。</p>
<p><a href="https://golang.org/pkg/context/" title="context - The Go Programming Language"><code>context</code></a> パッケージを使ったキャンセルの伝搬については以下を参照のこと。</p>
<ul>
<li><a href="https://text.baldanders.info/golang/ticker/">time.Ticker で遊ぶ</a></li>
</ul>
<h2>ワイルドカードを含むファイルの検索</h2>
<p><a href="https://github.com/goark/gocli" title="goark/gocli: Minimal Packages for Command-Line Interface"><code>gocli</code></a><code>/file</code> パッケージを使ったファイル検索は標準の <a href="https://golang.org/pkg/path/filepath/" title="filepath - The Go Programming Language"><code>filepath</code></a><code>.Glob()</code> 関数を拡張する形で実装している。
こんな感じに使える。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/goark/gocli/file"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">result</span> <span class="o">:=</span> <span class="nx">file</span><span class="p">.</span><span class="nf">Glob</span><span class="p">(</span><span class="s">"**/*.[ch]"</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">result</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Output:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// [testdata/include/source.h testdata/source.c]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p><code>file.Glob()</code> 関数の第2引数には検索時の条件を設定できる。
こんな感じ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/goark/gocli/file"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">result</span> <span class="o">:=</span> <span class="nx">file</span><span class="p">.</span><span class="nf">Glob</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"**/*.[ch]"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">file</span><span class="p">.</span><span class="nf">NewGlobOption</span><span class="p">(</span><span class="nx">file</span><span class="p">.</span><span class="nf">WithFlags</span><span class="p">(</span><span class="nx">file</span><span class="p">.</span><span class="nx">GlobStdFlags</span><span class="p">|</span><span class="nx">file</span><span class="p">.</span><span class="nx">GlobAbsolutePath</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">result</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Output:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// [/home/username/work/gocli/file/testdata/include/source.h /home/username/work/gocli/file/testdata/source.c]
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>指定できるフラグは以下の通り。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//Operation flag in Glob() function.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">const</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">GlobContainsFile</span> <span class="nx">GlobFlag</span> <span class="p">=</span> <span class="mi">1</span> <span class="o"><<</span> <span class="kc">iota</span>
</span></span><span class="line"><span class="cl"> <span class="nx">GlobContainsDir</span>
</span></span><span class="line"><span class="cl"> <span class="nx">GlobSeparatorSlash</span>
</span></span><span class="line"><span class="cl"> <span class="nx">GlobAbsolutePath</span>
</span></span><span class="line"><span class="cl"> <span class="nx">GlobStdFlags</span> <span class="p">=</span> <span class="nx">GlobContainsFile</span> <span class="p">|</span> <span class="nx">GlobContainsDir</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></div><p><code>file.Glob()</code> 関数の第2引数に <code>nil</code> をセットするか <code>file.NewGlobOption()</code> を引数なしで呼び出した場合は <code>file.GlobStdFlags</code> のみがセットされる。</p>
<p><a href="https://github.com/goark/gocli" title="goark/gocli: Minimal Packages for Command-Line Interface"><code>gocli</code></a><code>/file</code> パッケージはファイル操作の練習用に作ったもので,それなりには使えるとは思うが,正直に言って素朴すぎて効率はよくない。
実際に使うにはもう少しアレンジが必要になるだろう。</p>
<h2>設定ファイルのパスを取得する</h2>
<p><a href="https://go.dev/">Go</a> 1.13 から <a href="https://golang.org/pkg/os/" title="os - The Go Programming Language"><code>os</code></a><code>.UserConfigDir()</code> 関数が追加されたので,これを使って設定ファイルのパスを取得するパッケージ <a href="https://github.com/goark/gocli" title="goark/gocli: Minimal Packages for Command-Line Interface"><code>gocli</code></a><code>/config</code> を作ってみた。
こんな感じで使う。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/goark/gocli/config"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">path</span> <span class="o">:=</span> <span class="nx">config</span><span class="p">.</span><span class="nf">Path</span><span class="p">(</span><span class="s">"app"</span><span class="p">,</span> <span class="s">"config.json"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Output:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// /home/username/.config/app/config.json
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>アプリケーション名を指定して設定用ディレクトリのパスを取得する <code>config.Dir(appName string)</code> 関数も用意した。</p>
<p><a href="https://golang.org/pkg/os/" title="os - The Go Programming Language"><code>os</code></a><code>.UserConfigDir()</code> 関数で取得したパスにアプリケーション名と設定ファイル名をくっ付けただけの簡単なお仕事である。
<a href="https://go.dev/">Go</a> 1.13 では <a href="https://golang.org/pkg/os/" title="os - The Go Programming Language"><code>os</code></a><code>.UserConfigDir()</code> 関数は以下のように記述されている。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// UserConfigDir returns the default root directory to use for user-specific
</span></span></span><span class="line"><span class="cl"><span class="c1">// configuration data. Users should create their own application-specific
</span></span></span><span class="line"><span class="cl"><span class="c1">// subdirectory within this one and use that.
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// On Unix systems, it returns $XDG_CONFIG_HOME as specified by
</span></span></span><span class="line"><span class="cl"><span class="c1">// https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html if
</span></span></span><span class="line"><span class="cl"><span class="c1">// non-empty, else $HOME/.config.
</span></span></span><span class="line"><span class="cl"><span class="c1">// On Darwin, it returns $HOME/Library/Application Support.
</span></span></span><span class="line"><span class="cl"><span class="c1">// On Windows, it returns %AppData%.
</span></span></span><span class="line"><span class="cl"><span class="c1">// On Plan 9, it returns $home/lib.
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// If the location cannot be determined (for example, $HOME is not defined),
</span></span></span><span class="line"><span class="cl"><span class="c1">// then it will return an error.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">UserConfigDir</span><span class="p">()</span> <span class="p">(</span><span class="kt">string</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">dir</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="nx">runtime</span><span class="p">.</span><span class="nx">GOOS</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="s">"windows"</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">dir</span> <span class="p">=</span> <span class="nf">Getenv</span><span class="p">(</span><span class="s">"AppData"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">dir</span> <span class="o">==</span> <span class="s">""</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">""</span><span class="p">,</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"%AppData% is not defined"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="s">"darwin"</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">dir</span> <span class="p">=</span> <span class="nf">Getenv</span><span class="p">(</span><span class="s">"HOME"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">dir</span> <span class="o">==</span> <span class="s">""</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">""</span><span class="p">,</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"$HOME is not defined"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">dir</span> <span class="o">+=</span> <span class="s">"/Library/Application Support"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="s">"plan9"</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">dir</span> <span class="p">=</span> <span class="nf">Getenv</span><span class="p">(</span><span class="s">"home"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">dir</span> <span class="o">==</span> <span class="s">""</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">""</span><span class="p">,</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"$home is not defined"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">dir</span> <span class="o">+=</span> <span class="s">"/lib"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="p">:</span> <span class="c1">// Unix
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">dir</span> <span class="p">=</span> <span class="nf">Getenv</span><span class="p">(</span><span class="s">"XDG_CONFIG_HOME"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">dir</span> <span class="o">==</span> <span class="s">""</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">dir</span> <span class="p">=</span> <span class="nf">Getenv</span><span class="p">(</span><span class="s">"HOME"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">dir</span> <span class="o">==</span> <span class="s">""</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">""</span><span class="p">,</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"neither $XDG_CONFIG_HOME nor $HOME are defined"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">dir</span> <span class="o">+=</span> <span class="s">"/.config"</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">dir</span><span class="p">,</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2>キャッシュ用のディレクトリ・ファイルのパスを取得する</h2>
<p><a href="https://golang.org/pkg/os/" title="os - The Go Programming Language"><code>os</code></a><code>.UserCacheDir()</code> 関数を使ってキャッシュ用のディレクトリ・ファイルのパスを取得するパッケージ <a href="https://github.com/goark/gocli" title="goark/gocli: Minimal Packages for Command-Line Interface"><code>gocli</code></a><code>/cache</code> を作ってみた。
こんな感じで使う。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/goark/gocli/cache"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">path</span> <span class="o">:=</span> <span class="nx">cache</span><span class="p">.</span><span class="nf">Path</span><span class="p">(</span><span class="s">"app"</span><span class="p">,</span> <span class="s">"access.log"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Output:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// /home/username/.cache/app/access.log
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p><a href="https://golang.org/pkg/os/" title="os - The Go Programming Language"><code>os</code></a><code>.UserCacheDir()</code> 関数で取得したパスにアプリケーション名とファイル名をくっ付けただけの簡単なお仕事である。
<a href="https://go.dev/">Go</a> 1.13 では <a href="https://golang.org/pkg/os/" title="os - The Go Programming Language"><code>os</code></a><code>.UserCacheDir()</code> 関数は以下のように記述されている。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// UserCacheDir returns the default root directory to use for user-specific
</span></span></span><span class="line"><span class="cl"><span class="c1">// cached data. Users should create their own application-specific subdirectory
</span></span></span><span class="line"><span class="cl"><span class="c1">// within this one and use that.
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// On Unix systems, it returns $XDG_CACHE_HOME as specified by
</span></span></span><span class="line"><span class="cl"><span class="c1">// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html if
</span></span></span><span class="line"><span class="cl"><span class="c1">// non-empty, else $HOME/.cache.
</span></span></span><span class="line"><span class="cl"><span class="c1">// On Darwin, it returns $HOME/Library/Caches.
</span></span></span><span class="line"><span class="cl"><span class="c1">// On Windows, it returns %LocalAppData%.
</span></span></span><span class="line"><span class="cl"><span class="c1">// On Plan 9, it returns $home/lib/cache.
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// If the location cannot be determined (for example, $HOME is not defined),
</span></span></span><span class="line"><span class="cl"><span class="c1">// then it will return an error.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">UserCacheDir</span><span class="p">()</span> <span class="p">(</span><span class="kt">string</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">dir</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="nx">runtime</span><span class="p">.</span><span class="nx">GOOS</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="s">"windows"</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">dir</span> <span class="p">=</span> <span class="nf">Getenv</span><span class="p">(</span><span class="s">"LocalAppData"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">dir</span> <span class="o">==</span> <span class="s">""</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">""</span><span class="p">,</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"%LocalAppData% is not defined"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="s">"darwin"</span><span class="p">,</span> <span class="s">"ios"</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">dir</span> <span class="p">=</span> <span class="nf">Getenv</span><span class="p">(</span><span class="s">"HOME"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">dir</span> <span class="o">==</span> <span class="s">""</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">""</span><span class="p">,</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"$HOME is not defined"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">dir</span> <span class="o">+=</span> <span class="s">"/Library/Caches"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="s">"plan9"</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">dir</span> <span class="p">=</span> <span class="nf">Getenv</span><span class="p">(</span><span class="s">"home"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">dir</span> <span class="o">==</span> <span class="s">""</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">""</span><span class="p">,</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"$home is not defined"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">dir</span> <span class="o">+=</span> <span class="s">"/lib/cache"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="p">:</span> <span class="c1">// Unix
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">dir</span> <span class="p">=</span> <span class="nf">Getenv</span><span class="p">(</span><span class="s">"XDG_CACHE_HOME"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">dir</span> <span class="o">==</span> <span class="s">""</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">dir</span> <span class="p">=</span> <span class="nf">Getenv</span><span class="p">(</span><span class="s">"HOME"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">dir</span> <span class="o">==</span> <span class="s">""</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">""</span><span class="p">,</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"neither $XDG_CACHE_HOME nor $HOME are defined"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">dir</span> <span class="o">+=</span> <span class="s">"/.cache"</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">dir</span><span class="p">,</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
spiegel-im-spiegel/gocli v0.10.1 のリリース
tag:text.Baldanders.info,2019-09-07:/release/2019/09/gocli-v0_10_1-is-released/
2019-09-07T02:55:58+00:00
2021-12-04T02:40:05+00:00
書き直した結果 os.UserConfigDir() 関数で取得したパスにアプリケーション名と設定ファイル名をくっ付けただけの簡単なお仕事になった(笑)
Spiegel
https://baldanders.info/profile/
<p>Go 言語用 CLI プログラミング支援パッケージ <a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Minimal Packages for Command-Line Interface"><code>gocli</code></a> v0.10.1 をリリースした。</p>
<ul>
<li><a href="https://github.com/spiegel-im-spiegel/gocli/releases/tag/v0.10.1">Release v0.10.1 · spiegel-im-spiegel/gocli · GitHub</a></li>
</ul>
<p>実は『<a href="https://www.amazon.co.jp/exec/obidos/ASIN/B07VPSXF6N/baldandersinf-22/">改訂2版 みんなのGo言語</a>』を読んで「そろそろ設定ファイルの置き場所を何とかしなくちゃ」と思ってこっそり <a href="https://github.com/spiegel-im-spiegel/gocli/releases/tag/v0.10.0" title="Release v0.10.0 · spiegel-im-spiegel/gocli">v0.10.0</a> をリリースしたのだが, <a href="https://go.dev/">Go</a> 1.13 で <a href="https://golang.org/pkg/os/" title="os - The Go Programming Language"><code>os</code></a><code>.UserConfigDir()</code> 関数が用意されているのを知って「もうこれでいいぢゃん」てな感じになり,書き直した。こんな感じに使える。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gocli/config"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">path</span> <span class="o">:=</span> <span class="nx">config</span><span class="p">.</span><span class="nf">Path</span><span class="p">(</span><span class="s">"app"</span><span class="p">,</span> <span class="s">"config.json"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Output:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// /home/username/.config/app/config.json
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>書き直した結果 <a href="https://golang.org/pkg/os/" title="os - The Go Programming Language"><code>os</code></a><code>.UserConfigDir()</code> 関数で取得したパスにアプリケーション名と設定ファイル名をくっ付けただけの簡単なお仕事になった(笑)
私は設定ファイルと CLI との連携に <a href="https://github.com/spf13/viper" title="spf13/viper: Go configuration with fangs">spf13/viper</a> を使っているので,これで必要十分。</p>
<p><a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Minimal Packages for Command-Line Interface"><code>gocli</code></a> の使い方について詳しくは以下を参照のこと。</p>
<ul>
<li><a href="https://text.baldanders.info/release/gocli-package-for-golang/">Go 言語用 CLI プログラミング支援パッケージ</a></li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B07VPSXF6N?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51jif840ScL._SL160_.jpg" width="113" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B07VPSXF6N?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">改訂2版 みんなのGo言語</a></dt>
<dd>松木 雅幸 (著), mattn (著), 藤原 俊一郎 (著), 中島 大一 (著), 上田 拓也 (著), 牧 大輔 (著), 鈴木 健太 (著)</dd>
<dd>技術評論社 2019-08-01 (Release 2019-08-01)</dd>
<dd>Kindle版</dd>
<dd>B07VPSXF6N (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="4"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="far fa-star"></i></abbr></dd>
</dl>
<p class="description">改訂2版の目玉は7章の「データベースの扱い方」が追加されたことだろう。他の章では,大まかな構成は1版と同じだが細かい部分が変わっていて Go 1.12 への言及まであるのには驚いた。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2019-08-12">2019-08-12</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 改訂2版 みんなのGo言語 -->
gjq v0.2.0 をリリースした
tag:text.Baldanders.info,2019-03-17:/release/2019/03/gjq-v0_2_0-is-released/
2019-03-17T12:34:01+00:00
2021-08-12T21:22:05+00:00
2つほどオプションを追加した。
Spiegel
https://baldanders.info/profile/
<p><a href="https://text.baldanders.info/release/2019/03/gjq/" title="jq ぽい何か を書いてみた">昨日</a>の今日で恐縮だが [spiegel-im-spiegel/gjq] v0.2.0 をリリースした。</p>
<ul>
<li><a href="https://github.com/spiegel-im-spiegel/gjq/releases/tag/v0.2.0">Release v0.2.0 · spiegel-im-spiegel/gjq · GitHub</a></li>
</ul>
<p>2つほどオプションを追加した。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ gjq -h
</span></span><span class="line"><span class="cl">Usage:
</span></span><span class="line"><span class="cl"> gjq [flags] <filter string>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Flags:
</span></span><span class="line hl"><span class="cl"> -c, --clipboard copy to clipboard in interactive mode
</span></span><span class="line hl"><span class="cl"> -C, --color colorize JSON
</span></span><span class="line"><span class="cl"> --debug for debug
</span></span><span class="line"><span class="cl"> -f, --file string JSON data (file path)
</span></span><span class="line"><span class="cl"> -h, --help help for gjq
</span></span><span class="line"><span class="cl"> -i, --indent int indent size for formatted JSON string (default 2)
</span></span><span class="line"><span class="cl"> -I, --interactive interactive mode
</span></span><span class="line"><span class="cl"> -t, --tab use tabs for indentation
</span></span><span class="line"><span class="cl"> -u, --url string JSON data (URL)
</span></span><span class="line"><span class="cl"> -v, --version output version of gjq</span></span></code></pre></div>
<p>小文字の <code>-c</code> をつけると対話モード時にフィルタリング結果をクリップボードにコピーする。
逆にこのオプションを付けないとクリップボードへのコピーは行わないようにした。
対話モードは <code>Ctrl+C</code> を入力するまで続くが,この割り込みタイミングで直前の結果をクリップボードにコピーする。</p>
<p>大文字の <code>-C</code> オプションでフィルタリング結果をカラー表示できるようにした。
ファイルやクリップボードへの出力には影響しない(筈)。</p>
<p>JSON 文字列のカラー化には以下の2つのパッケージを利用している。</p>
<ul>
<li><a href="https://github.com/fatih/color">fatih/color: Color package for Go (golang)</a></li>
<li><a href="https://github.com/nwidger/jsoncolor">nwidger/jsoncolor: Colorized JSON output for Go https://godoc.org/github.com/nwidger/jsoncolor</a></li>
</ul>
<p>いやぁ,おかげさまで実装がめっさ簡単だったわ(笑)</p>
<p>あと Windows とそれ以外のプラットフォームとの差を吸収するために</p>
<ul>
<li><a href="https://github.com/mattn/go-colorable">mattn/go-colorable</a></li>
</ul>
<p>を利用している。
たとえば</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">colorable</span><span class="p">.</span><span class="nf">NewColorableStdout</span><span class="p">(),</span> <span class="nb">string</span><span class="p">(</span><span class="nx">coloredJSONbytes</span><span class="p">))</span>
</span></span></code></pre></div><p>とかすればプラットフォームに関係なく <a href="https://en.wikipedia.org/wiki/ANSI_escape_code" title="ANSI escape code - Wikipedia">ANSI 標準のエスケープシーケンス</a>を使って文字に色を付けることができる。</p>
<p>これで「jq ぽい何か」でやりたいことはだいたい実装できたので本来の作業へ戻るとしよう。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="https://text.baldanders.info/release/2019/03/gjq/">jq ぽい何か を書いてみた</a></li>
</ul>
<p>[spiegel-im-spiegel/gjq]: <a href="https://github.com/spiegel-im-spiegel/gjq">https://github.com/spiegel-im-spiegel/gjq</a> “spiegel-im-spiegel/gjq: Another Implementation of “jq” by golang”</p>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
jq ぽい何か を書いてみた
tag:text.Baldanders.info,2019-03-16:/release/2019/03/gjq/
2019-03-16T14:12:32+00:00
2021-08-12T21:22:05+00:00
私が Windows を捨てれば simeji/jid で全然 OK なのだが,鼻の先はそういうわけにもいかないので,自分用に CLI を書いてみることにした。
Spiegel
https://baldanders.info/profile/
<p>ウソです。
ほとんど自力じゃありません(笑)</p>
<p>JSON データを操作できる <a href="https://stedolan.github.io/jq/">jq</a> はとても高機能で使い勝手もいいのだが,純粋にフィルタとして機能するため Windows 環境では微妙に使いづらい場合がある。
具体的には</p>
<ul>
<li>入力が標準入力のみなのでリダイレクトやパイプと組み合わせる必要がある</li>
<li>対話モードがない</li>
</ul>
<p>といったところ。
大した問題ではないのだがどうにかならないかなぁ,と思っていた。</p>
<p>んで,どなたか <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>で書いている人がいるんじゃないかと思って探してみたら以下のツール/パッケージを見つけた。</p>
<ul>
<li><a href="https://github.com/simeji/jid" title="simeji/jid: json incremental digger">simeji/jid</a>
<ul>
<li><a href="https://qiita.com/simeji/items/dd0464b7ed91c51ee618">JSONをインタラクティブに掘り下げるコマンド jid - Qiita</a></li>
</ul>
</li>
<li><a href="https://github.com/savaki/jq" title="savaki/jq: A high performance Golang implementation of the incredibly useful jq command line tool.">savaki/jq</a></li>
<li><a href="https://github.com/apang1992/jq" title="apang1992/jq: json query with golang">apang1992/jq</a></li>
</ul>
<p><a href="https://github.com/simeji/jid" title="simeji/jid: json incremental digger">simeji/jid</a> はよく出来ていると思うのだが,残念なことに <a href="https://conemu.github.io/" title="ConEmu - Handy Windows Terminal">ConEmu</a> 上で動かすと表示がぶっ壊れる(コマンドプロンプトでは問題ない)。
<a href="https://github.com/savaki/jq" title="savaki/jq: A high performance Golang implementation of the incredibly useful jq command line tool.">savaki/jq</a> と <a href="https://github.com/apang1992/jq" title="apang1992/jq: json query with golang">apang1992/jq</a> はパッケージだが,ここ2,3年は更新されていないようである。</p>
<p>私が Windows を捨てれば <a href="https://github.com/simeji/jid" title="simeji/jid: json incremental digger">simeji/jid</a> で全然 OK なのだが(そのうち捨てる),鼻の先はそういうわけにもいかないので,自分用に CLI を書いてみることにした。</p>
<ul>
<li><a href="https://github.com/spiegel-im-spiegel/gjq" title="spiegel-im-spiegel/gjq: Another Implementation of "jq" by golang">spiegel-im-spiegel/gjq</a></li>
</ul>
<p>コマンドライン・オプションは以下の通り。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ gjq -h
</span></span><span class="line"><span class="cl">Usage:
</span></span><span class="line"><span class="cl"> gjq [flags] <filter string>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Flags:
</span></span><span class="line"><span class="cl"> --debug for debug
</span></span><span class="line"><span class="cl"> -f, --file string JSON data (file path)
</span></span><span class="line"><span class="cl"> -h, --help help for gjq
</span></span><span class="line"><span class="cl"> -i, --indent int indent size for formatted JSON string (default 2)
</span></span><span class="line"><span class="cl"> -I, --interactive interactive mode
</span></span><span class="line"><span class="cl"> -t, --tab use tabs for indentation
</span></span><span class="line"><span class="cl"> -u, --url string JSON data (URL)
</span></span><span class="line"><span class="cl"> -v, --version output version of gjq
</span></span></code></pre></div><p>パーサには <a href="https://github.com/savaki/jq" title="savaki/jq: A high performance Golang implementation of the incredibly useful jq command line tool.">savaki/jq</a> を使っている。
フィルタの文法は以下の通り。</p>
<table>
<thead>
<tr>
<th>syntax</th>
<th>meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>.</code></td>
<td>unchanged input</td>
</tr>
<tr>
<td><code>.foo</code></td>
<td>value at key</td>
</tr>
<tr>
<td><code>.foo.bar</code></td>
<td>value at nested key</td>
</tr>
<tr>
<td><code>.[0]</code></td>
<td>value at specified element of array</td>
</tr>
<tr>
<td><code>.[0:1]</code></td>
<td>array of specified elements of array, inclusive</td>
</tr>
<tr>
<td><code>.foo.[0]</code></td>
<td>nested value</td>
</tr>
</tbody>
</table>
<p>まずは <a href="https://stedolan.github.io/jq/">jq</a> と同じようにフィルタとして使うならこんな感じ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ cat testdata/test.json
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> "string": "a",
</span></span><span class="line"><span class="cl"> "number": 1.23,
</span></span><span class="line"><span class="cl"> "simple": ["a", "b", "c"],
</span></span><span class="line"><span class="cl"> "mixed": [
</span></span><span class="line"><span class="cl"> "a",
</span></span><span class="line"><span class="cl"> 1,
</span></span><span class="line"><span class="cl"> {"hello":"world"}
</span></span><span class="line"><span class="cl"> ],
</span></span><span class="line"><span class="cl"> "object": {
</span></span><span class="line"><span class="cl"> "first": "joe",
</span></span><span class="line"><span class="cl"> "array": [1,2,3]
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ gjq .object.array < testdata/test.json
</span></span><span class="line"><span class="cl">[
</span></span><span class="line"><span class="cl"> 1,
</span></span><span class="line"><span class="cl"> 2,
</span></span><span class="line"><span class="cl"> 3
</span></span><span class="line"><span class="cl">]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ cat testdata/test.json | gjq .mixed
</span></span><span class="line"><span class="cl">[
</span></span><span class="line"><span class="cl"> "a",
</span></span><span class="line"><span class="cl"> 1,
</span></span><span class="line"><span class="cl"> {
</span></span><span class="line"><span class="cl"> "hello": "world"
</span></span><span class="line"><span class="cl"> }
</span></span><span class="line"><span class="cl">]
</span></span></code></pre></div><p>インデントは <code>-i</code> オプションで調整できるが 0 をセットすると</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ gjq -i 0 .mixed < testdata/test.json
</span></span><span class="line"><span class="cl">["a",1,{"hello":"world"}]
</span></span></code></pre></div><p>とコンパクトになる。</p>
<p>また <code>-f</code> オプションでファイルを直接指定することもできる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ gjq -f testdata/test.json .object.array
</span></span><span class="line"><span class="cl">[
</span></span><span class="line"><span class="cl"> 1,
</span></span><span class="line"><span class="cl"> 2,
</span></span><span class="line"><span class="cl"> 3
</span></span><span class="line"><span class="cl">]
</span></span></code></pre></div><p>さらに <code>-u</code> オプションで URL を指定すれば Web 上のファイルも対象になる<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ gjq -u https://text.baldanders.info/index.json .entry.[0]
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> "title": "猫を殺すに猫を以ってせよ",
</span></span><span class="line"><span class="cl"> "section": "remark",
</span></span><span class="line"><span class="cl"> "description": "分かるかな。分っかんねーだろうな(反語)",
</span></span><span class="line"><span class="cl"> "author": "Spiegel",
</span></span><span class="line"><span class="cl"> "license": "http://creativecommons.org/licenses/by-sa/4.0/",
</span></span><span class="line"><span class="cl"> "url": "https://text.baldanders.info/remark/2019/03/no-cat-no-life/",
</span></span><span class="line"><span class="cl"> "published": "2019-03-11T13:51:41+00:00",
</span></span><span class="line"><span class="cl"> "update": "2019-03-11T13:53:40+00:00"
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>さらにさらに <code>-I</code> オプションを付けると対話モードになる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ gjq -u https://text.baldanders.info/index.json -I
</span></span><span class="line"><span class="cl">Press Ctrl+C to stop
</span></span><span class="line"><span class="cl">Filter> .title
</span></span><span class="line"><span class="cl">"text.Baldanders.info"
</span></span><span class="line"><span class="cl">Filter> .entry.[0]
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl"> "title": "猫を殺すに猫を以ってせよ",
</span></span><span class="line"><span class="cl"> "section": "remark",
</span></span><span class="line"><span class="cl"> "description": "分かるかな。分っかんねーだろうな(反語)",
</span></span><span class="line"><span class="cl"> "author": "Spiegel",
</span></span><span class="line"><span class="cl"> "license": "http://creativecommons.org/licenses/by-sa/4.0/",
</span></span><span class="line"><span class="cl"> "url": "https://text.baldanders.info/remark/2019/03/no-cat-no-life/",
</span></span><span class="line"><span class="cl"> "published": "2019-03-11T13:51:41+00:00",
</span></span><span class="line"><span class="cl"> "update": "2019-03-11T13:53:40+00:00"
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">Filter>
</span></span></code></pre></div><p>対話モードではクリップボードにも出力されるのでターミナル上でコピペする必要はない。
また JSON データは起動時に1度だけ GET するので,安心して対話モードで弄れる(笑)</p>
<p>さて次は色を付けてみるかな。
<a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>でターミナル出力に色を付けるってどうやるんだろ。
調べてみるか。</p>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><code>-u</code> オプションは HTTP/HTTPS の GET で対象を取得する。 POST には対応してないのであしからず。 POST で取ってくる必要があるなら潔く curl を使ったほうがいいだろう。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
spiegel-im-spiegel/gocli v0.9.1 をリリースした
tag:text.Baldanders.info,2019-02-09:/release/2019/02/gocli-v0_9_1-is-released/
2019-02-09T13:00:33+00:00
2021-08-12T21:22:05+00:00
対話モードで動かしたいツールがあって,それ用に使えるサブパッケージ gocli/prompt を追加してみた。
Spiegel
https://baldanders.info/profile/
<p>CLI で使える細々した機能を詰め合わせた自作パッケージ <a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Minimal Packages for Command-Line Interface">spiegel-im-spiegel/gocli</a> の v0.9.1 をリリースした。</p>
<ul>
<li><a href="https://github.com/spiegel-im-spiegel/gocli">spiegel-im-spiegel/gocli: Minimal Packages for Command-Line Interface</a></li>
</ul>
<p>対話モードで動かしたいツールがあって,それ用に使えるサブパッケージ <a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Minimal Packages for Command-Line Interface"><code>gocli</code></a><code>/prompt</code> を追加してみた。</p>
<p>こんな感じに書ける。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gocli/prompt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gocli/rwi"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">p</span> <span class="o">:=</span> <span class="nx">prompt</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithReader</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdin</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithWriter</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="kd">func</span><span class="p">(</span><span class="nx">s</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="kt">string</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">s</span> <span class="o">==</span> <span class="s">"q"</span> <span class="o">||</span> <span class="nx">s</span> <span class="o">==</span> <span class="s">"quit"</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">"quit prompt"</span><span class="p">,</span> <span class="nx">prompt</span><span class="p">.</span><span class="nx">ErrTerminate</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">runes</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">rune</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">j</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">runes</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">j</span><span class="p">;</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">j</span> <span class="p">=</span> <span class="nx">i</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="nx">j</span><span class="o">-</span><span class="mi">1</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">runes</span><span class="p">[</span><span class="nx">i</span><span class="p">],</span> <span class="nx">runes</span><span class="p">[</span><span class="nx">j</span><span class="p">]</span> <span class="p">=</span> <span class="nx">runes</span><span class="p">[</span><span class="nx">j</span><span class="p">],</span> <span class="nx">runes</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">string</span><span class="p">(</span><span class="nx">runes</span><span class="p">),</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="nx">prompt</span><span class="p">.</span><span class="nf">WithPromptString</span><span class="p">(</span><span class="s">"Sample> "</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="nx">prompt</span><span class="p">.</span><span class="nf">WithHeaderMessage</span><span class="p">(</span><span class="s">"Input 'q' or 'quit' to stop"</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">p</span><span class="p">.</span><span class="nf">IsTerminal</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="s">"not terminal (or pipe?)"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">p</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>このコードでは入力した文字列を逆順に並べ替えて出力する。
“<code>q</code>” または “<code>quit</code>” を入力するとプログラムを終了する。</p>
<p>これを動かすとこんな感じ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run prompt/sample/sample.go
</span></span><span class="line"><span class="cl">Input 'q' or 'quit' to stop
</span></span><span class="line"><span class="cl">Sample> abcdef
</span></span><span class="line"><span class="cl">fedcba
</span></span><span class="line"><span class="cl">Sample> quit
</span></span><span class="line"><span class="cl">quit prompt
</span></span></code></pre></div><p>ちなみに <a href="https://github.com/atotto/clipboard" title="atotto/clipboard: clipboard for golang">github.com/atotto/clipboard</a> と組み合わせて</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line hl"><span class="cl"> <span class="s">"github.com/atotto/clipboard"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gocli/prompt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gocli/rwi"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">p</span> <span class="o">:=</span> <span class="nx">prompt</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithReader</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdin</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithWriter</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="kd">func</span><span class="p">(</span><span class="nx">s</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="kt">string</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">s</span> <span class="o">==</span> <span class="s">"q"</span> <span class="o">||</span> <span class="nx">s</span> <span class="o">==</span> <span class="s">"quit"</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">"quit prompt"</span><span class="p">,</span> <span class="nx">prompt</span><span class="p">.</span><span class="nx">ErrTerminate</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">runes</span> <span class="o">:=</span> <span class="p">[]</span><span class="nb">rune</span><span class="p">(</span><span class="nx">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">j</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="nx">runes</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="nx">i</span> <span class="p"><</span> <span class="nx">j</span><span class="p">;</span> <span class="nx">i</span><span class="p">,</span> <span class="nx">j</span> <span class="p">=</span> <span class="nx">i</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="nx">j</span><span class="o">-</span><span class="mi">1</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">runes</span><span class="p">[</span><span class="nx">i</span><span class="p">],</span> <span class="nx">runes</span><span class="p">[</span><span class="nx">j</span><span class="p">]</span> <span class="p">=</span> <span class="nx">runes</span><span class="p">[</span><span class="nx">j</span><span class="p">],</span> <span class="nx">runes</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">res</span> <span class="o">:=</span> <span class="nb">string</span><span class="p">(</span><span class="nx">runes</span><span class="p">)</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">return</span> <span class="nx">res</span><span class="p">,</span> <span class="nx">clipboard</span><span class="p">.</span><span class="nf">WriteAll</span><span class="p">(</span><span class="nx">res</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="nx">prompt</span><span class="p">.</span><span class="nf">WithPromptString</span><span class="p">(</span><span class="s">"Sample> "</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="nx">prompt</span><span class="p">.</span><span class="nf">WithHeaderMessage</span><span class="p">(</span><span class="s">"Input 'q' or 'quit' to stop"</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">p</span><span class="p">.</span><span class="nf">IsTerminal</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="s">"not terminal (or pipe?)"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">p</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
<p>とか書けば標準出力への出力と同時にクリップボードにも出力される。</p>
<p><a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Minimal Packages for Command-Line Interface">spiegel-im-spiegel/gocli</a> パッケージは CLI を組む際に(主に自分が)便利な細々とした機能を収録している。
他の人には使いにくいかもしれないし大した内容でもないため <a href="https://creativecommons.org/publicdomain/zero/1.0/" title="Creative Commons — CC0 1.0 Universal">CC0</a> で公開している。
一切の権利を放棄しているので自由に持っていって弄っていただいて構わない。</p>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
Gocli Package v0.8.0 Released
tag:text.Baldanders.info,2018-03-08:/release/2018/03/gocli-package-v0_8_0-released/
2018-03-08T13:28:19+00:00
2021-08-12T21:22:05+00:00
Ruby の Dir.glob にあるようなディレクトリの再帰検索ができる file.Glob() 関数を作ってみた。
Spiegel
https://baldanders.info/profile/
<p>この前 <a href="https://text.baldanders.info/release/2018/03/gocli-package-v0_7_0-released/">v0.7.0 をリリースした</a>ばかりだが,舌の根も乾かぬうちに v0.8.0 をリリースしてしまった。</p>
<ul>
<li><a href="https://github.com/spiegel-im-spiegel/gocli/releases/tag/v0.8.0">Release v0.8.0 Released · spiegel-im-spiegel/gocli</a></li>
</ul>
<p>今回はファイル・ディレクトリ操作用の <a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Minimal Packages for Command-Line Interface"><code>gocli</code></a><code>/file</code> サブパッケージを追加した。</p>
<p>実は <a href="https://github.com/mattn/jvgrep" title="mattn/jvgrep: grep for japanese vimmer">mattn/jvgrep</a> を使ってみてディレクトリの再帰検索はなかなか便利なことに気づき,自分でも実装してみようと思ったのだ。</p>
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>標準の <a href="https://golang.org/pkg/path/filepath/" title="filepath - The Go Programming Language"><code>filepath</code></a><code>.Glob()</code> 関数はなかなか性能がよくて,パス検索に以下のワイルドカードが使える。</p>
<figure style='margin:0 auto;text-align:center;'><pre tabindex="0"><code>pattern:
{ term }
term:
'*' matches any sequence of non-Separator characters
'?' matches any single non-Separator character
'[' [ '^' ] { character-range } ']'
character class (must be non-empty)
c matches character c (c != '*', '?', '\\', '[')
'\\' c matches character c
<p>character-range:
c matches character c (c != '\', '-', ']')
'\' c matches character c
lo '-' hi matches character c for lo <= c <= hi
</code></pre></p>
<figcaption><div><a href="https://golang.org/pkg/path/filepath/">filepath - The Go Programming Language</a></div></figcaption>
</figure>
<p>しかし,残念ながら Ruby の <a href="https://docs.ruby-lang.org/ja/latest/class/Dir.html" title="Class: Dir (Ruby 2.5.0)"><code>Dir</code></a><code>.glob</code> にあるようなディレクトリの再帰検索(<code>**/</code>)は用意されていない。
そこで今回 <a href="https://golang.org/pkg/path/filepath/" title="filepath - The Go Programming Language"><code>filepath</code></a><code>.Glob()</code> と <a href="https://golang.org/pkg/path/filepath/" title="filepath - The Go Programming Language"><code>filepath</code></a><code>.Walk()</code> を組み合わせる形で <code>file.Glob()</code> 関数を作ってみた。</p>
<p>こんなふうに使える。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gocli/file"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Args</span><span class="p">)</span> <span class="p"><</span> <span class="mi">2</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="nx">os</span><span class="p">.</span><span class="nx">ErrInvalid</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">path</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Args</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">paths</span> <span class="o">:=</span> <span class="nx">file</span><span class="p">.</span><span class="nf">Glob</span><span class="p">(</span><span class="nx">path</span><span class="p">,</span> <span class="nx">file</span><span class="p">.</span><span class="nf">NewGlobOption</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">paths</span><span class="p">)</span> <span class="p">></span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">path</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">paths</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>これを実行するとこんな感じになる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run /path/to/search.go **/*.lua
</span></span><span class="line"><span class="cl">nyagos.d\aliasandset.lua
</span></span><span class="line"><span class="cl">nyagos.d\aliases.lua
</span></span><span class="line"><span class="cl">nyagos.d\backquote.lua
</span></span><span class="line"><span class="cl">nyagos.d\box.lua
</span></span><span class="line"><span class="cl">nyagos.d\brace.lua
</span></span><span class="line"><span class="cl">nyagos.d\catalog\autocd.lua
</span></span><span class="line"><span class="cl">nyagos.d\catalog\autols.lua
</span></span><span class="line"><span class="cl">nyagos.d\catalog\cho.lua
</span></span><span class="line"><span class="cl">nyagos.d\catalog\dollar.lua
</span></span><span class="line"><span class="cl">nyagos.d\catalog\ezoe.lua
</span></span><span class="line"><span class="cl">nyagos.d\catalog\git.lua
</span></span><span class="line"><span class="cl">nyagos.d\catalog\gogit.lua
</span></span><span class="line"><span class="cl">nyagos.d\catalog\neco.lua
</span></span><span class="line"><span class="cl">nyagos.d\catalog\nyagosini.lua
</span></span><span class="line"><span class="cl">nyagos.d\catalog\peco.lua
</span></span><span class="line"><span class="cl">nyagos.d\catalog\subcomplete.lua
</span></span><span class="line"><span class="cl">nyagos.d\cdlnk.lua
</span></span><span class="line"><span class="cl">nyagos.d\comspec.lua
</span></span><span class="line"><span class="cl">nyagos.d\lns.lua
</span></span><span class="line"><span class="cl">nyagos.d\open.lua
</span></span><span class="line"><span class="cl">nyagos.d\start.lua
</span></span><span class="line"><span class="cl">nyagos.d\su.lua
</span></span><span class="line"><span class="cl">nyagos.d\suffix.lua
</span></span><span class="line"><span class="cl">nyagos.d\swapstdfunc.lua
</span></span><span class="line"><span class="cl">nyagos.d\trash.lua
</span></span><span class="line"><span class="cl">nyagos.d\use.lua
</span></span></code></pre></div><p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>製なので,フォルダの区切り文字には <code>'/'</code> と <code>'\'</code> の両方が使える。
また出力結果の内,フォルダについては末尾にフォルダ区切り文字(<code>'/'</code> または <code>'\'</code>)を付加している。
その他,細かい使い方については <code>glob_test.go</code> を参照のこと。</p>
<p>本当はもう少しスマートなコードを書きたかったんだけど,行き当たりばったりで書いてたらエラく汚いコードになってしまった。
まぁ,これが今の私の実力,ということで。
そのうち refactoring できる機会もあるかもしれない。</p>
<p><a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Minimal Packages for Command-Line Interface">spiegel-im-spiegel/gocli</a> パッケージは CLI (Command-Line Interface) を組む際に(主に自分が)便利な細々とした機能を収録している。
他の人には使いにくいかもしれないし大した内容でもないため <a href="https://creativecommons.org/publicdomain/zero/1.0/" title="Creative Commons — CC0 1.0 Universal">CC0</a> で公開している。
一切の権利を放棄しているので自由に持っていって弄っていただいて構わない。</p>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
Gocli Package v0.7.0 Released
tag:text.Baldanders.info,2018-03-03:/release/2018/03/gocli-package-v0_7_0-released/
2018-03-03T12:12:41+00:00
2021-08-12T21:22:05+00:00
v0.7.0 では SIGNAL 制御を行う gocli/signal サブパッケージを追加した。 具体的には context パッケージと組み合わせてキャンセル・イベントとして実装している。
Spiegel
https://baldanders.info/profile/
<p><a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Minimal Packages for Command-Line Interface">spiegel-im-spiegel/gocli</a> パッケージ v0.7.0 をリリースした。</p>
<ul>
<li><a href="https://github.com/spiegel-im-spiegel/gocli">spiegel-im-spiegel/gocli: Minimal Packages for Command-Line Interface</a></li>
</ul>
<p>v0.7.0 では <a href="https://linuxjm.osdn.jp/html/LDP_man-pages/man7/signal.7.html" title="Man page of SIGNAL">SIGNAL</a> 制御を行う <a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Minimal Packages for Command-Line Interface"><code>gocli</code></a><code>/signal</code> サブパッケージを追加した。
具体的には <a href="https://golang.org/pkg/context/" title="context - The Go Programming Language">context</a> パッケージと組み合わせてキャンセル・イベントとして実装している。</p>
<p>例えば,こんな感じで使う。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"context"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"time"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gocli/signal"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">ticker</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span> <span class="o">:=</span> <span class="nx">time</span><span class="p">.</span><span class="nf">NewTicker</span><span class="p">(</span><span class="mi">1</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span> <span class="c1">// 1 second cycle
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">defer</span> <span class="nx">t</span><span class="p">.</span><span class="nf">Stop</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">select</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">now</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">t</span><span class="p">.</span><span class="nx">C</span><span class="p">:</span> <span class="c1">// ticker event
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">now</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">RFC3339</span><span class="p">))</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">case</span> <span class="o"><-</span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Done</span><span class="p">():</span> <span class="c1">// cancel event from context
</span></span></span><span class="line hl"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Stop ticker"</span><span class="p">)</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">return</span> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Err</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Run</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">errCh</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kt">error</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nb">close</span><span class="p">(</span><span class="nx">errCh</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">child</span><span class="p">,</span> <span class="nx">cancelChild</span> <span class="o">:=</span> <span class="nx">context</span><span class="p">.</span><span class="nf">WithTimeout</span><span class="p">(</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">signal</span><span class="p">.</span><span class="nf">Context</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nf">Background</span><span class="p">(),</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Interrupt</span><span class="p">),</span> <span class="c1">// cancel event by SIGNAL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="mi">10</span><span class="o">*</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">,</span> <span class="c1">// timeout after 10 seconds
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nf">cancelChild</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">errCh</span> <span class="o"><-</span> <span class="nf">ticker</span><span class="p">(</span><span class="nx">child</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="o">:=</span> <span class="o"><-</span><span class="nx">errCh</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Done"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">Run</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
<p>Windows 環境では <a href="https://linuxjm.osdn.jp/html/LDP_man-pages/man7/signal.7.html" title="Man page of SIGNAL">SIGNAL</a> 周りのテストが出来ないので結構困ってたり。</p>
<p><a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Minimal Packages for Command-Line Interface">spiegel-im-spiegel/gocli</a> パッケージは CLI (Command-Line Interface) を組む際に(主に自分が)便利な細々とした機能を収録している。
他の人には使いにくいかもしれないし大した内容でもないため <a href="https://creativecommons.org/publicdomain/zero/1.0/" title="Creative Commons — CC0 1.0 Universal">CC0</a> で公開している。
一切の権利を放棄しているので自由に持っていって弄っていただいて構わない。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="https://text.baldanders.info/golang/ticker/">time.Ticker で遊ぶ</a></li>
<li><a href="https://text.baldanders.info/golang/cli-and-facade-pattern/">コマンドライン・インタフェースとファサード・パターン</a></li>
<li><a href="https://text.baldanders.info/golang/using-and-testing-cobra/">Cobra の使い方とテスト</a></li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
Cobra の使い方とテスト
tag:text.Baldanders.info,2017-12-06:/golang/using-and-testing-cobra/
2017-12-06T12:01:33+00:00
2021-08-12T21:22:05+00:00
spf13/cobra そのものについてちゃんと書いてない気がするので,今回はコードの書き方からテストまでをひと通り紹介していく。
Spiegel
https://baldanders.info/profile/
<p>だいぶ <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> の扱いに慣れてきたので,そろそろブログにまとめておこうかなと。
以前に CLI (Command-Line Interface) とファサード・パターンについては以下の記事に</p>
<ul>
<li><a href="https://text.baldanders.info/golang/cli-and-facade-pattern/">コマンドライン・インタフェースとファサード・パターン</a></li>
</ul>
<p><a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> の使い方については,触りの部分を以下の記事に書いた。</p>
<ul>
<li><a href="https://text.baldanders.info/golang/estimate-of-pi-2-cli/">モンテカルロ法による円周率の推定(その2 CLI)</a></li>
<li><a href="https://text.baldanders.info/golang/codic-api/">Codic API を利用するパッケージを作ってみた</a> (<a href="https://github.com/spf13/viper" title="spf13/viper: Go configuration with fangs">spf13/viper</a> との連携について)</li>
</ul>
<p>ただ <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> そのものについてちゃんと書いてない気がするので,今回はコードの書き方からテストまでをひと通り紹介していく。
漫然と紹介するのもアレなので,今回は以下の CLI を作ることを目標にする。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ cli-demo show -i 123 -s 文字列 -b
</span></span><span class="line"><span class="cl">Integer option value: 123
</span></span><span class="line"><span class="cl"> String option value: 文字列
</span></span><span class="line"><span class="cl">Boolean option value: true
</span></span></code></pre></div><p>なお,この記事の作業の結果は以下のリポジトリに置いてある。</p>
<ul>
<li><a href="https://github.com/spiegel-im-spiegel/cli-demo">spiegel-im-spiegel/cli-demo: Demonstration for Command Line Interface</a></li>
</ul>
<h2><a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> のインストール</h2>
<p><a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> には CLI ツールもあるのだがバイナリは提供されていないので,自前で取ってきてコンパイルする。
といっても</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go get -u github.com/spf13/cobra/cobra
</span></span></code></pre></div><p>とするだけだが。
<code>github.com/spf13/cobra</code> で get しようとすると CLI のコンパイルを行わないので注意が必要である。
CLI の Usage はこんな感じ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ cobra -h
</span></span><span class="line"><span class="cl">Cobra is a CLI library for Go that empowers applications.
</span></span><span class="line"><span class="cl">This application is a tool to generate the needed files
</span></span><span class="line"><span class="cl">to quickly create a Cobra application.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Usage:
</span></span><span class="line"><span class="cl"> cobra [command]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Available Commands:
</span></span><span class="line"><span class="cl"> add Add a command to a Cobra Application
</span></span><span class="line"><span class="cl"> help Help about any command
</span></span><span class="line"><span class="cl"> init Initialize a Cobra Application
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Flags:
</span></span><span class="line"><span class="cl"> -a, --author string author name for copyright attribution (default "YOUR NAME")
</span></span><span class="line"><span class="cl"> --config string config file (default is $HOME/.cobra.yaml)
</span></span><span class="line"><span class="cl"> -h, --help help for cobra
</span></span><span class="line"><span class="cl"> -l, --license string name of license for the project
</span></span><span class="line"><span class="cl"> --viper use Viper for configuration (default true)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Use "cobra [command] --help" for more information about a command.
</span></span></code></pre></div><h2>初期化処理</h2>
<p>まずは <code>init</code> サブコマンドで雛形となるソースコードを展開する。
ソースを展開したいフォルダで</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">cobra --viper=false init .
</span></span></code></pre></div><p>とする。
今回は <a href="https://github.com/spf13/viper" title="spf13/viper: Go configuration with fangs">spf13/viper</a> は使わないので <code>--viper</code> オプションは無効にしておく。</p>
<p>これで対象のフォルダに3つのファイルが出力される</p>
<ul>
<li><code>main.go</code></li>
<li><code>LICENSE</code></li>
<li><code>cmd/root.go</code></li>
</ul>
<p><code>LICENSE</code> はライセンス・ファイルなのでそのまま利用するなり他のファイルに差し替えるなりすればいい。
<code>main.go</code> と <code>cmd/root.go</code> が実際の雛形になるのだが,やたらコメントが多いので,整理したものを以下に示す。</p>
<p>まずは <code>main.go</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"github.com/spiegel-im-spiegel/cli-demo/cmd"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cmd</span><span class="p">.</span><span class="nf">Execute</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>次に <code>cmd/root.go</code>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">cmd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"os"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spf13/cobra"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// RootCmd represents the base command when called without any subcommands
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">RootCmd</span> <span class="p">=</span> <span class="o">&</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Use</span><span class="p">:</span> <span class="s">"cli-demo"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Short</span><span class="p">:</span> <span class="s">"Short comment"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Long</span><span class="p">:</span> <span class="s">"Long comment"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//Run: func(cmd *cobra.Command, args []string) { },
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//Execute is called from main function
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Execute</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">RootCmd</span><span class="p">.</span><span class="nf">Execute</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">os</span><span class="p">.</span><span class="nf">Exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>呼び出し関係は以下の通り。</p>
<figure style='margin:0 auto;text-align:center;'>
<div class="mermaid">
graph LR
main["main()"]
exec1["cmd.Execute()"]
exec2["cmd.RootCmd.Execute()"]
main-->exec1
exec1-->exec2
</div></figure>
<p>もちろん <code>main()</code> 関数から直接 <code>cmd.RootCmd.Execute()</code> 関数を呼んでも構わない(このままであれば)。
この状態でコマンドを起動すると以下のようになる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run main.go -h
</span></span><span class="line"><span class="cl">Long comment
</span></span></code></pre></div><p>まぁ,当然だよね。
現状での問題は3つある。</p>
<ol>
<li><code>cmd.RootCmd</code> を他パッケージから直接操作できてしまう</li>
<li>引数と標準入出力を <code>cmd.RootCmd</code> が握っていてコントロールできない(ように見える)</li>
<li><code>cmd.RootCmd.Execute()</code> 関数がエラー時に <code>os.Exit()</code> 関数で強制終了してしまう</li>
</ol>
<p>特に2番目が致命的。
何故ならこのままではテストができないからだ。
そこで <code>cmd.Execute()</code> 関数を改造することにする。</p>
<h2><a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Command line interface">spiegel-im-spiegel/gocli</a> の導入</h2>
<p>その前に標準入出力の取り回しを簡単にするために,手前味噌で申し訳ないが, <a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Command line interface">spiegel-im-spiegel/gocli</a> を導入する。
これを使えば標準入出力をひとつのインスタンスで取り回しできる<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gocli/exitcode"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gocli/rwi"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">run</span><span class="p">(</span><span class="nx">ui</span> <span class="o">*</span><span class="nx">rwi</span><span class="p">.</span><span class="nx">RWI</span><span class="p">)</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">ExitCode</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ui</span><span class="p">.</span><span class="nf">Outputln</span><span class="p">(</span><span class="s">"Hello world"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">Normal</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">run</span><span class="p">(</span><span class="nx">rwi</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithReader</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdin</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithWriter</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithErrorWriter</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="p">)).</span><span class="nf">Exit</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>rwi.New()</code> の初期化は Functional Options パターンを使っている。
Functional Options パターンについて詳しくは以下を参照のこと。</p>
<ul>
<li><a href="https://text.baldanders.info/golang/functional-options-pattern/">インスタンスの生成と Functional Options パターン</a></li>
</ul>
<h2>cmd.Execute() 関数を改造する</h2>
<p>では <code>cmd.Execute()</code> 関数の改造を行おう。
途中を省いて結果は以下の通り。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">var</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span> <span class="p">=</span> <span class="nx">rwi</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span> <span class="c1">//CUI instance
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//Execute is called from main function
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Execute</span><span class="p">(</span><span class="nx">ui</span> <span class="o">*</span><span class="nx">rwi</span><span class="p">.</span><span class="nx">RWI</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="nx">exit</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">ExitCode</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//panic hundling
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">r</span> <span class="o">:=</span> <span class="nb">recover</span><span class="p">();</span> <span class="nx">r</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">OutputErrln</span><span class="p">(</span><span class="s">"Panic:"</span><span class="p">,</span> <span class="nx">r</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">depth</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="p">;</span> <span class="nx">depth</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">pc</span><span class="p">,</span> <span class="nx">src</span><span class="p">,</span> <span class="nx">line</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">runtime</span><span class="p">.</span><span class="nf">Caller</span><span class="p">(</span><span class="nx">depth</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">break</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">OutputErrln</span><span class="p">(</span><span class="s">" ->"</span><span class="p">,</span> <span class="nx">depth</span><span class="p">,</span> <span class="s">":"</span><span class="p">,</span> <span class="nx">runtime</span><span class="p">.</span><span class="nf">FuncForPC</span><span class="p">(</span><span class="nx">pc</span><span class="p">).</span><span class="nf">Name</span><span class="p">(),</span> <span class="s">":"</span><span class="p">,</span> <span class="nx">src</span><span class="p">,</span> <span class="s">":"</span><span class="p">,</span> <span class="nx">line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">exit</span> <span class="p">=</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">Abnormal</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">//execution
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">cui</span> <span class="p">=</span> <span class="nx">ui</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rootCmd</span><span class="p">.</span><span class="nf">SetArgs</span><span class="p">(</span><span class="nx">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rootCmd</span><span class="p">.</span><span class="nf">SetOutput</span><span class="p">(</span><span class="nx">ui</span><span class="p">.</span><span class="nf">ErrorWriter</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="nx">exit</span> <span class="p">=</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">Normal</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">rootCmd</span><span class="p">.</span><span class="nf">Execute</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">exit</span> <span class="p">=</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">Abnormal</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>前半は <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">panic</a> 時のハンドリングで,スタック追跡できるようにしている。
<a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">panic</a> 時のハンドリングについて詳しくは以下を参照のこと。</p>
<ul>
<li><a href="https://text.baldanders.info/golang/stack-trace-and-panic-handling/">スタック追跡とパニック・ハンドリング</a></li>
</ul>
<p>後半では <code>cmd.rootCmd</code> に引数と標準出力をセットしてから <code>cmd.rootCmd.Execute()</code> 関数を起動している<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。
エラー時は <code>os.Exit()</code> 関数で強制終了するのではなく,ちゃんとステータスを返すようにした。</p>
<p>そうそう。
<code>cmd.RootCmd</code> ではパッケージ外から直接操作できてしまうので <code>cmd.rootCmd</code> と小文字にしている。
小さなことからコツコツと(笑)</p>
<p>これで <code>main()</code> 関数もこのように書き換えることができる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cmd</span><span class="p">.</span><span class="nf">Execute</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithReader</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdin</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithWriter</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithErrorWriter</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="nx">os</span><span class="p">.</span><span class="nx">Args</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span>
</span></span><span class="line"><span class="cl"> <span class="p">).</span><span class="nf">Exit</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2>サブコマンドの追加</h2>
<p>ではいよいよサブコマンド <code>show</code> を追加する。
サブコマンドの追加は <code>main.go</code> のあるフォルダで以下のコマンドを実行する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ cobra add show
</span></span></code></pre></div><p>これにより <code>cmd/show.go</code> ファイルが追加された。
これも中身はコメントが山ほどあるので,整理したものを以下に示す。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">cmd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spf13/cobra"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// showCmd represents the show command
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">showCmd</span> <span class="p">=</span> <span class="o">&</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Use</span><span class="p">:</span> <span class="s">"show"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Short</span><span class="p">:</span> <span class="s">"Short comment for show sub-command"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Long</span><span class="p">:</span> <span class="s">"Long comment for show sub-command"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Run</span><span class="p">:</span> <span class="kd">func</span><span class="p">(</span><span class="nx">cmd</span> <span class="o">*</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"show called"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rootCmd</span><span class="p">.</span><span class="nf">AddCommand</span><span class="p">(</span><span class="nx">showCmd</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>この状態で実際に動かしてみるとこうなる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run main.go -h
</span></span><span class="line"><span class="cl">Long comment
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Usage:
</span></span><span class="line"><span class="cl"> cli-demo [flags]
</span></span><span class="line"><span class="cl"> cli-demo [command]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Available Commands:
</span></span><span class="line"><span class="cl"> help Help about any command
</span></span><span class="line"><span class="cl"> show Short comment for show sub-command
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Flags:
</span></span><span class="line"><span class="cl"> -h, --help help for cli-demo
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Use "cli-demo [command] --help" for more information about a command.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ go run main.go show -h
</span></span><span class="line"><span class="cl">Long comment for show sub-command
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Usage:
</span></span><span class="line"><span class="cl"> cli-demo show [flags]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Flags:
</span></span><span class="line"><span class="cl"> -h, --help help for show
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ go run main.go show
</span></span><span class="line"><span class="cl">show called
</span></span></code></pre></div><h3>オプションの追加</h3>
<p>このコードに対してオプションを追加していく。
こんな感じでいいだろう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">cmd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spf13/cobra"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// showCmd represents the show command
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="nx">showCmd</span> <span class="p">=</span> <span class="o">&</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Use</span><span class="p">:</span> <span class="s">"show"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Short</span><span class="p">:</span> <span class="s">"Short comment for show sub-command"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Long</span><span class="p">:</span> <span class="s">"Long comment for show sub-command"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">RunE</span><span class="p">:</span> <span class="kd">func</span><span class="p">(</span><span class="nx">cmd</span> <span class="o">*</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">i</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">cmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">GetInt</span><span class="p">(</span><span class="s">"integer"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">cmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">GetBool</span><span class="p">(</span><span class="s">"boolean"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">s</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">cmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">GetString</span><span class="p">(</span><span class="s">"string"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">Outputln</span><span class="p">(</span><span class="s">"Integer option value:"</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">Outputln</span><span class="p">(</span><span class="s">" String option value:"</span><span class="p">,</span> <span class="nx">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">Outputln</span><span class="p">(</span><span class="s">"Boolean option value:"</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rootCmd</span><span class="p">.</span><span class="nf">AddCommand</span><span class="p">(</span><span class="nx">showCmd</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">showCmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">IntP</span><span class="p">(</span><span class="s">"integer"</span><span class="p">,</span> <span class="s">"i"</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"integer option"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">showCmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">BoolP</span><span class="p">(</span><span class="s">"boolean"</span><span class="p">,</span> <span class="s">"b"</span><span class="p">,</span> <span class="kc">false</span><span class="p">,</span> <span class="s">"boolean option"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">showCmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">StringP</span><span class="p">(</span><span class="s">"string"</span><span class="p">,</span> <span class="s">"s"</span><span class="p">,</span> <span class="s">""</span><span class="p">,</span> <span class="s">"string option"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>cmd.showCmd</code> 内の <code>Run</code> が <code>RunE</code> に変わってる点に注目。
これにより,関数内で error が発生した場合に,それを返り値で渡すことができる。</p>
<p>試しにちょろんと動かしてみよう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ go run main.go show -h
</span></span><span class="line"><span class="cl">Long comment for show sub-command
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Usage:
</span></span><span class="line"><span class="cl"> cli-demo show [flags]
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Flags:
</span></span><span class="line"><span class="cl"> -b, --boolean boolean option
</span></span><span class="line"><span class="cl"> -h, --help help for show
</span></span><span class="line"><span class="cl"> -i, --integer int integer option
</span></span><span class="line"><span class="cl"> -s, --string string string option
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">$ go run main.go show -i 123 -s 日本語 -b
</span></span><span class="line"><span class="cl">Integer option value: 123
</span></span><span class="line"><span class="cl"> String option value: 日本語
</span></span><span class="line"><span class="cl">Boolean option value: true
</span></span></code></pre></div><p>おおっ。
なんか動いてるような気がする。</p>
<p>じゃあテストを始めようか。</p>
<h2>cobra.Command を返す関数を作る。</h2>
<p>と言いたいところだけど,このままではまだテストができない。
何故かというと, <code>cmd.rootCmd</code> も <code>cmd.showCmd</code> も static な変数として定義されているので,そのままテストを繰り返すと前回の状態が残ってしまって正しいテストにならないからだ。</p>
<p>じゃあどうすればいいかというと, <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions"><code>cobra</code></a><code>.Command</code> を返す関数を作って,その中で <code>cmd.rootCmd</code> や <code>cmd.showCmd</code> に相当するインスタンスを作ればいいのである。</p>
<p>じゃあ,まずは <code>cmd/root.go</code> の完全版から。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">cmd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"runtime"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/pkg/errors"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spf13/cobra"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gocli/exitcode"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gocli/rwi"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span> <span class="p">=</span> <span class="nx">rwi</span><span class="p">.</span><span class="nf">New</span><span class="p">()</span> <span class="c1">//CUI instance
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//newRootCmd returns cobra.Command instance for root command
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">newRootCmd</span><span class="p">(</span><span class="nx">ui</span> <span class="o">*</span><span class="nx">rwi</span><span class="p">.</span><span class="nx">RWI</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="o">*</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span> <span class="p">=</span> <span class="nx">ui</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rootCmd</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Use</span><span class="p">:</span> <span class="s">"cli-demo"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Short</span><span class="p">:</span> <span class="s">"Short comment"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Long</span><span class="p">:</span> <span class="s">"Long comment"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">RunE</span><span class="p">:</span> <span class="kd">func</span><span class="p">(</span><span class="nx">cmd</span> <span class="o">*</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"no command"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rootCmd</span><span class="p">.</span><span class="nf">SetArgs</span><span class="p">(</span><span class="nx">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rootCmd</span><span class="p">.</span><span class="nf">SetOutput</span><span class="p">(</span><span class="nx">ui</span><span class="p">.</span><span class="nf">ErrorWriter</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">rootCmd</span><span class="p">.</span><span class="nf">AddCommand</span><span class="p">(</span><span class="nf">newShowCmd</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">rootCmd</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//Execute is called from main function
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Execute</span><span class="p">(</span><span class="nx">ui</span> <span class="o">*</span><span class="nx">rwi</span><span class="p">.</span><span class="nx">RWI</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="nx">exit</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">ExitCode</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//panic hundling
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">r</span> <span class="o">:=</span> <span class="nb">recover</span><span class="p">();</span> <span class="nx">r</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">OutputErrln</span><span class="p">(</span><span class="s">"Panic:"</span><span class="p">,</span> <span class="nx">r</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">depth</span> <span class="o">:=</span> <span class="mi">0</span><span class="p">;</span> <span class="p">;</span> <span class="nx">depth</span><span class="o">++</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">pc</span><span class="p">,</span> <span class="nx">src</span><span class="p">,</span> <span class="nx">line</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">runtime</span><span class="p">.</span><span class="nf">Caller</span><span class="p">(</span><span class="nx">depth</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">break</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">OutputErrln</span><span class="p">(</span><span class="s">" ->"</span><span class="p">,</span> <span class="nx">depth</span><span class="p">,</span> <span class="s">":"</span><span class="p">,</span> <span class="nx">runtime</span><span class="p">.</span><span class="nf">FuncForPC</span><span class="p">(</span><span class="nx">pc</span><span class="p">).</span><span class="nf">Name</span><span class="p">(),</span> <span class="s">":"</span><span class="p">,</span> <span class="nx">src</span><span class="p">,</span> <span class="s">":"</span><span class="p">,</span> <span class="nx">line</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">exit</span> <span class="p">=</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">Abnormal</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">//execution
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">exit</span> <span class="p">=</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">Normal</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">newRootCmd</span><span class="p">(</span><span class="nx">ui</span><span class="p">,</span> <span class="nx">args</span><span class="p">).</span><span class="nf">Execute</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">exit</span> <span class="p">=</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">Abnormal</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>次は <code>cmd/show.go</code> の完全版。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">cmd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spf13/cobra"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//newShowCmd returns cobra.Command instance for show sub-command
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">newShowCmd</span><span class="p">()</span> <span class="o">*</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">showCmd</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Use</span><span class="p">:</span> <span class="s">"show"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Short</span><span class="p">:</span> <span class="s">"Short comment for show sub-command"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Long</span><span class="p">:</span> <span class="s">"Long comment for show sub-command"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nx">RunE</span><span class="p">:</span> <span class="kd">func</span><span class="p">(</span><span class="nx">cmd</span> <span class="o">*</span><span class="nx">cobra</span><span class="p">.</span><span class="nx">Command</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">i</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">cmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">GetInt</span><span class="p">(</span><span class="s">"integer"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">cmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">GetBool</span><span class="p">(</span><span class="s">"boolean"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">s</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">cmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">GetString</span><span class="p">(</span><span class="s">"string"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">Outputln</span><span class="p">(</span><span class="s">"Integer option value:"</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">Outputln</span><span class="p">(</span><span class="s">" String option value:"</span><span class="p">,</span> <span class="nx">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">cui</span><span class="p">.</span><span class="nf">Outputln</span><span class="p">(</span><span class="s">"Boolean option value:"</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"> <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">showCmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">IntP</span><span class="p">(</span><span class="s">"integer"</span><span class="p">,</span> <span class="s">"i"</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s">"integer option"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">showCmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">BoolP</span><span class="p">(</span><span class="s">"boolean"</span><span class="p">,</span> <span class="s">"b"</span><span class="p">,</span> <span class="kc">false</span><span class="p">,</span> <span class="s">"boolean option"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">showCmd</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">StringP</span><span class="p">(</span><span class="s">"string"</span><span class="p">,</span> <span class="s">"s"</span><span class="p">,</span> <span class="s">""</span><span class="p">,</span> <span class="s">"string option"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">showCmd</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>なんで <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> が最初からこういう雛形を作ってくれないかというと,おそらく <code>cobra add</code> コマンドで <code>cmd/root.go</code> を触るわけにはいかないため(たぶん既にユーザが触ってる), static 変数にして <code>init()</code> 関数でコマンドを繋げるような組み方しかできなかったんだと思う。
でも手作業でこういう雛形を作ってしまえば以後はコピペでいくらでも量産できるので,テストのことを考えれば,面倒でも最初に手間を掛けたほうがいいかもしれない。</p>
<h2>テストを書く</h2>
<p>ようやくテストが書けるよ。
とりあえず正常系のみ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">cmd</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"bytes"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"testing"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gocli/exitcode"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"github.com/spiegel-im-spiegel/gocli/rwi"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">TestShowNormal</span><span class="p">(</span><span class="nx">t</span> <span class="o">*</span><span class="nx">testing</span><span class="p">.</span><span class="nx">T</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">testCases</span> <span class="o">:=</span> <span class="p">[]</span><span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span>
</span></span><span class="line"><span class="cl"> <span class="nx">want</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"> <span class="p">}{</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">args</span><span class="p">:</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"show"</span><span class="p">,</span> <span class="s">"-i"</span><span class="p">,</span> <span class="s">"123"</span><span class="p">,</span> <span class="s">"-s"</span><span class="p">,</span> <span class="s">"日本語"</span><span class="p">,</span> <span class="s">"-b"</span><span class="p">},</span> <span class="nx">want</span><span class="p">:</span> <span class="s">"Integer option value: 123\n String option value: 日本語\nBoolean option value: true\n"</span><span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">args</span><span class="p">:</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"show"</span><span class="p">,</span> <span class="s">"-i"</span><span class="p">,</span> <span class="s">"123"</span><span class="p">,</span> <span class="s">"-s"</span><span class="p">,</span> <span class="s">"日本語"</span><span class="p">},</span> <span class="nx">want</span><span class="p">:</span> <span class="s">"Integer option value: 123\n String option value: 日本語\nBoolean option value: false\n"</span><span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">args</span><span class="p">:</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"show"</span><span class="p">,</span> <span class="s">"-i"</span><span class="p">,</span> <span class="s">"123"</span><span class="p">},</span> <span class="nx">want</span><span class="p">:</span> <span class="s">"Integer option value: 123\n String option value: \nBoolean option value: false\n"</span><span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span><span class="nx">args</span><span class="p">:</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"show"</span><span class="p">},</span> <span class="nx">want</span><span class="p">:</span> <span class="s">"Integer option value: 0\n String option value: \nBoolean option value: false\n"</span><span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">c</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">testCases</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">out</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">errOut</span> <span class="o">:=</span> <span class="nb">new</span><span class="p">(</span><span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ui</span> <span class="o">:=</span> <span class="nx">rwi</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithWriter</span><span class="p">(</span><span class="nx">out</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="nx">rwi</span><span class="p">.</span><span class="nf">WithErrorWriter</span><span class="p">(</span><span class="nx">errOut</span><span class="p">),</span>
</span></span><span class="line"><span class="cl"> <span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">exit</span> <span class="o">:=</span> <span class="nf">Execute</span><span class="p">(</span><span class="nx">ui</span><span class="p">,</span> <span class="nx">c</span><span class="p">.</span><span class="nx">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">exit</span> <span class="o">!=</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">Normal</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">"Execute() err = \"%v\", want \"%v\"."</span><span class="p">,</span> <span class="nx">exit</span><span class="p">,</span> <span class="nx">exitcode</span><span class="p">.</span><span class="nx">Normal</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">out</span><span class="p">.</span><span class="nf">String</span><span class="p">()</span> <span class="o">!=</span> <span class="nx">c</span><span class="p">.</span><span class="nx">want</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">"Execute() Stdout = \"%v\", want \"%v\"."</span><span class="p">,</span> <span class="nx">out</span><span class="p">.</span><span class="nf">String</span><span class="p">(),</span> <span class="nx">c</span><span class="p">.</span><span class="nx">want</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">errOut</span><span class="p">.</span><span class="nf">String</span><span class="p">()</span> <span class="o">!=</span> <span class="s">""</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">t</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">"Execute() Stderr = \"%v\", want \"%v\"."</span><span class="p">,</span> <span class="nx">errOut</span><span class="p">.</span><span class="nf">String</span><span class="p">(),</span> <span class="s">""</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>標準出力と標準エラー出力を <a href="https://golang.org/pkg/bytes/" title="bytes - The Go Programming Language"><code>bytes</code></a><code>.Buffer</code> で代替えしているのがお分かりだろうか。
これなら CLI でもかなりの部分をテストでカバーできる。
ここには挙げていないが,当然パイプのテストも可能である。</p>
<h2><a href="https://github.com/golang/dep" title="golang/dep: Go dependency management tool">dep</a> で <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> を管理する</h2>
<p><a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> をはじめとする外部パッケージを <a href="https://github.com/golang/dep" title="golang/dep: Go dependency management tool">dep</a> を使って <code>vendor/</code> 配下に置くのなら, <code>Gopkg.toml</code> の記述は以下でいいだろう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/spf13/cobra"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"0.0.*"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/spiegel-im-spiegel/gocli"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"0.7.*"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/pkg/errors"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"0.8.*"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">prune</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">go-tests</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"> <span class="nx">unused-packages</span> <span class="p">=</span> <span class="kc">true</span>
</span></span></code></pre></div><p>ちなみに今回のデモのパッケージ依存関係はこんな感じになっている。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">$ dep status -dot | dot -Tpng -o dependency.png
</span></span></code></pre></div><figure style='margin:0 auto;text-align:center;'><a href="dependency.png"><img src="dependency.png" srcset="dependency.png 754w" sizes="(min-width:600px) 500px, 80vw" alt="" loading="lazy"></a></figure>
<h2>ブックマーク</h2>
<ul>
<li><a href="https://qiita.com/tkit/items/3cdeafcde2bd98612428">Golangのコマンドライブラリcobraを使って少しうまく実装する - Qiita</a> : <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions"><code>cobra</code></a><code>.Command</code> の関数化のアイデアはこちらからいただいた。感謝</li>
<li><a href="https://qiita.com/izumin5210/items/b06a81002a6934c05185">cobra / pflags でフラグをパースせずに args に残す - Qiita</a></li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B07VPSXF6N?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51jif840ScL._SL160_.jpg" width="113" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B07VPSXF6N?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">改訂2版 みんなのGo言語</a></dt>
<dd>松木 雅幸 (著), mattn (著), 藤原 俊一郎 (著), 中島 大一 (著), 上田 拓也 (著), 牧 大輔 (著), 鈴木 健太 (著)</dd>
<dd>技術評論社 2019-08-01 (Release 2019-08-01)</dd>
<dd>Kindle版</dd>
<dd>B07VPSXF6N (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="4"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="far fa-star"></i></abbr></dd>
</dl>
<p class="description">改訂2版の目玉は7章の「データベースの扱い方」が追加されたことだろう。他の章では,大まかな構成は1版と同じだが細かい部分が変わっていて Go 1.12 への言及まであるのには驚いた。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2019-08-12">2019-08-12</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 改訂2版 みんなのGo言語 -->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>コードを見れば分かると思うが <a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Command line interface">spiegel-im-spiegel/gocli</a> には最小限のコードしかないので,たとえば複数の <a href="http://golang.org/ref/spec#Go_statements">goroutine</a> が動いている状態で単一の入出力インスタンスを取り回すとか考えたくもない。なので,取り扱いには十分注意すること。その代わり <a href="https://github.com/spiegel-im-spiegel/gocli" title="spiegel-im-spiegel/gocli: Command line interface">spiegel-im-spiegel/gocli</a> では <a href="https://creativecommons.org/publicdomain/zero/1.0/" title="Creative Commons — CC0 1.0 Universal">CC0</a> つまり著作者人格権も含めて完全に権利を放棄しているので,自由に使っていただいて構わない。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p><a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions"><code>cobra</code></a><code>.Command</code> には出力先がひとつしかなく何故か標準出力と標準エラー出力を区別していない。そこで <code>cmd.rootCmd</code> には標準エラー出力をセットして標準出力へのアクセスは <code>cui</code> を <code>cmd</code> パッケージ内のどのメソッドからも参照できるようにした(つまり,少なくとも <code>cmd</code> パッケージ内は単一の <a href="http://golang.org/ref/spec#Go_statements">goroutine</a> で動くことが前提)。 <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> 側が標準出力と標準エラー出力をちゃんと区別してくれれば,もう少しスマートにできるんだけどねぇ。(あと標準入力用の Reader も付けて欲しい。まぁなくても何とかなってるけど<code>w</code>) <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Codic API を利用するパッケージを作ってみた
tag:text.Baldanders.info,2017-10-25:/golang/codic-api/
2017-10-25T06:46:59+00:00
2019-07-01T13:48:09+00:00
spf13/viper を使ってみたかったのだ。
Spiegel
https://baldanders.info/profile/
<p>つい最近まで知らなかったのだが <a href="https://codic.jp/" title="プログラマーのためのネーミング辞書 | codic">codic</a> というサービスがあるらしい。</p>
<ul>
<li><a href="https://codic.jp/">プログラマーのためのネーミング辞書 | codic</a></li>
</ul>
<p>簡単に言うと日本語の「名前」を英語に変換してくれるサービスなのだが,プログラマ向けに変数名やメソッド名として使いやすいよう提案してくれる優れものである。
まさに<a href="https://text.baldanders.info/remark/2017/10/programmin-language-in-english/" title="プログラミング言語の暗黙ルール">英語不得手な私</a>のためにあるようなサービスじゃないか! 何故今までこのサービスに辿り着けなかったのか <code>orz</code></p>
<p>Web 画面はこんな感じ。</p>
<figure style='margin:0 auto;text-align:center;'><a href="https://photo.baldanders.info/flickr/37176009973/"><img src="https://photo.baldanders.info/flickr/image/37176009973_m.png" srcset="https://photo.baldanders.info/flickr/image/37176009973_m.png 500w" sizes="(min-width:600px) 500px, 80vw" alt="codic service" loading="lazy"></a><figcaption><div><a href="https://photo.baldanders.info/flickr/37176009973/">codic service</a></div></figcaption>
</figure>
<p>あの時このサービスのことを知っていたら<a href="https://text.baldanders.info/remark/2017/04/regist-dose-not-exist/" title="“regist” という単語は存在しない">メソッド名に <code>regist</code></a> とか付けようとして赤っ恥をかかなくて済んだのに。
とほほ。</p>
<p>というわけで早速サインアップしましたよ。
GitHub のアカウントでもサインアップできるのが素敵(最終確認にメールアドレスを要求されるけど)。</p>
<p>で, <a href="https://codic.jp/" title="プログラマーのためのネーミング辞書 | codic">codic</a> では API を公開しているようだ。</p>
<ul>
<li><a href="https://codic.jp/docs/api">API | codic</a></li>
</ul>
<p>で,これを使うための <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>パッケージも既に見られるんだけど</p>
<ul>
<li><a href="https://github.com/codic-project/Codic_cli">codic-project/Codic_cli</a></li>
<li><a href="https://github.com/39e/go-codic">39e/go-codic</a></li>
</ul>
<p>CLI しか用意されてない,っていうか何で GET で取ろうとするんだよ! というわけで自作することにした。</p>
<p>→ 自作しました。</p>
<ul>
<li><a href="https://github.com/spiegel-im-spiegel/gocodic">spiegel-im-spiegel/gocodic: codic の API を利用するための Go 言語パッケージ</a></li>
</ul>
<p>すみません。
勢いで作ったのでテストを書いてません。
そのうちなんとかします。
日本語圏向けのサービスなんだから <a href="https://github.com/spiegel-im-spiegel/gocodic/blob/master/README.md" title="gocodic/README.md at master · spiegel-im-spiegel/gocodic">README</a> もガッツリ日本語でいいよね(笑)</p>
<h2>Curl で API を確認する</h2>
<p>RESTfull API なんだから <a href="http://curl.haxx.se/" title="curl and libcurl">curl</a> で説明してくれよ,と思う私は贅沢なのでしょうか。</p>
<ul>
<li><a href="https://shibukawa.github.io/curl_as_dsl/">cURL as DSL — cURL as DSL 1.0 documentation</a></li>
<li><a href="http://blog.shibu.jp/article/115602749.html">Shibu’s Diary: cURL as DSLとは何だったのか。あるいは細かすぎて伝わらないcURL as DSL。</a></li>
</ul>
<p>とりあえず,<a href="https://codic.jp/docs/api/engine/translate">翻訳用の API</a> は <a href="http://curl.haxx.se/" title="curl and libcurl">curl</a> を使うと以下のように記述できる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">curl "https://api.codic.jp/v1/engine/translate.json" -H "Authorization: Bearer YOUR_ACCESS_TOKEN" -F "text=hello" -F "casing=camel"
</span></span></code></pre></div><p><code>text=hello</code> って日本語やないやないかい! というのはとりあえずスルーしていただいて,これを <a href="https://shibukawa.github.io/curl_as_dsl/" title="cURL as DSL — cURL as DSL 1.0 documentation">cURL as DSL</a> で <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>コードに変換すると以下のようになる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"bytes"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"io/ioutil"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"log"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"mime/multipart"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"net/http"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">client</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">http</span><span class="p">.</span><span class="nx">Client</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">buffer</span> <span class="nx">bytes</span><span class="p">.</span><span class="nx">Buffer</span>
</span></span><span class="line"><span class="cl"> <span class="nx">writer</span> <span class="o">:=</span> <span class="nx">multipart</span><span class="p">.</span><span class="nf">NewWriter</span><span class="p">(</span><span class="o">&</span><span class="nx">buffer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">writer</span><span class="p">.</span><span class="nf">WriteField</span><span class="p">(</span><span class="s">"text"</span><span class="p">,</span> <span class="s">"hello"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">writer</span><span class="p">.</span><span class="nf">WriteField</span><span class="p">(</span><span class="s">"casing"</span><span class="p">,</span> <span class="s">"camel"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">writer</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">request</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">http</span><span class="p">.</span><span class="nf">NewRequest</span><span class="p">(</span><span class="s">"POST"</span><span class="p">,</span> <span class="s">"https://api.codic.jp/v1/engine/translate.json"</span><span class="p">,</span> <span class="o">&</span><span class="nx">buffer</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">request</span><span class="p">.</span><span class="nx">Header</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="s">"Authorization"</span><span class="p">,</span> <span class="s">"Bearer YOUR_ACCESS_TOKEN"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">request</span><span class="p">.</span><span class="nx">Header</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="s">"Content-Type"</span><span class="p">,</span> <span class="s">"multipart/form-data; boundary="</span><span class="o">+</span><span class="nx">writer</span><span class="p">.</span><span class="nf">Boundary</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">resp</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">client</span><span class="p">.</span><span class="nf">Do</span><span class="p">(</span><span class="nx">request</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">resp</span><span class="p">.</span><span class="nx">Body</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">body</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">ReadAll</span><span class="p">(</span><span class="nx">resp</span><span class="p">.</span><span class="nx">Body</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">log</span><span class="p">.</span><span class="nf">Print</span><span class="p">(</span><span class="nb">string</span><span class="p">(</span><span class="nx">body</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>これが雛形で出発点である。
<code>YOUR_ACCESS_TOKEN</code> に正しいアクセス・トークン(<a href="https://codic.jp/" title="プログラマーのためのネーミング辞書 | codic">codic</a> にサインアップするともらえる)をセットすればちゃんと動く。
動くコードってのは大事だよね。</p>
<p>最終的にどうなったかは <a href="https://github.com/spiegel-im-spiegel/gocodic/blob/master/README.md" title="gocodic/README.md at master · spiegel-im-spiegel/gocodic">README</a> を見ていただきたい。</p>
<h2>spf13/viper を使ってみたかったのだ</h2>
<p>外部パッケージは <a href="https://text.baldanders.info/golang/consider-switching-from-glide-to-dep/" title="Glide から Dep への移行を検討する">dep で管理</a>することにした。
こんな感じである。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/spf13/cobra"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">branch</span> <span class="p">=</span> <span class="s2">"master"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/spf13/viper"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"^1.0.0"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/spiegel-im-spiegel/gocli"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"^0.4.0"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">constraint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span> <span class="p">=</span> <span class="s2">"github.com/pkg/errors"</span>
</span></span><span class="line"><span class="cl"> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"^0.8.0"</span>
</span></span></code></pre></div><p><a href="https://github.com/pkg/errors" title="pkg/errors: Simple error handling primitives">pkg/errors</a> パッケージ以外は CLI (Command-Line Interface) 用のパッケージである。
この中でも今回は特に <a href="https://github.com/spf13/viper" title="spf13/viper: Go configuration with fangs">spf13/viper</a> を使ってみたかったのだ。
だって毎回アクセス・トークンをコマンドラインに書く訳にはいかないでしょ。
呼び出しバッチやスクリプトに書くとか以ての外だし。</p>
<p><a href="https://github.com/spf13/viper" title="spf13/viper: Go configuration with fangs">spf13/viper</a> は設定ファイルにアクセスするためのパッケージで,特に <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> との相性がいいのが特徴である。
というか同じ作者なのだが。
<a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> の使い方は以前紹介したので,そちらを参考にして欲しい。</p>
<ul>
<li><a href="https://text.baldanders.info/golang/estimate-of-pi-2-cli/">モンテカルロ法による円周率の推定(その2 CLI)</a></li>
</ul>
<p><a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> が生成してくれる <code>cmd/root.go</code> に <a href="https://github.com/spf13/viper" title="spf13/viper: Go configuration with fangs">spf13/viper</a> 初期化のコードがある。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// initConfig reads in config file and ENV variables if set.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">initConfig</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">cfgFile</span> <span class="o">!=</span> <span class="s">""</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Use config file from the flag.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">viper</span><span class="p">.</span><span class="nf">SetConfigFile</span><span class="p">(</span><span class="nx">cfgFile</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Find home directory.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">home</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">homedir</span><span class="p">.</span><span class="nf">Dir</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Print</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">os</span><span class="p">.</span><span class="nf">Exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Search config in home directory with name ".gocodic" (without extension).
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">viper</span><span class="p">.</span><span class="nf">AddConfigPath</span><span class="p">(</span><span class="nx">home</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">viper</span><span class="p">.</span><span class="nf">SetConfigName</span><span class="p">(</span><span class="s">".gocodic"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">viper</span><span class="p">.</span><span class="nf">AutomaticEnv</span><span class="p">()</span> <span class="c1">// read in environment variables that match
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"> <span class="c1">// If a config file is found, read it in.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">viper</span><span class="p">.</span><span class="nf">ReadInConfig</span><span class="p">();</span> <span class="nx">err</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Print</span><span class="p">(</span><span class="s">"Using config file:"</span><span class="p">,</span> <span class="nx">viper</span><span class="p">.</span><span class="nf">ConfigFileUsed</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>この関数は <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> が生成した <code>cmd</code> パッケージの <code>init()</code> 関数内で呼び出される。
このまま弄らなくても問題ないが,個人的にはエラーを標準出力に出してるのが気に入らなかったので少し変えている。</p>
<p>その後, <a href="https://github.com/spf13/viper" title="spf13/viper: Go configuration with fangs">spf13/viper</a> で読み込む設定項目を記述していくのだが</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">rootCmd</span><span class="p">.</span><span class="nf">PersistentFlags</span><span class="p">().</span><span class="nf">StringVar</span><span class="p">(</span><span class="o">&</span><span class="nx">cfgFile</span><span class="p">,</span> <span class="s">"config"</span><span class="p">,</span> <span class="s">""</span><span class="p">,</span> <span class="s">"config file (default is $HOME/.gocodic.yaml)"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">rootCmd</span><span class="p">.</span><span class="nf">PersistentFlags</span><span class="p">().</span><span class="nf">BoolP</span><span class="p">(</span><span class="s">"json"</span><span class="p">,</span> <span class="s">"j"</span><span class="p">,</span> <span class="kc">false</span><span class="p">,</span> <span class="s">"output by JSON format (raw data)"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">rootCmd</span><span class="p">.</span><span class="nf">PersistentFlags</span><span class="p">().</span><span class="nf">StringP</span><span class="p">(</span><span class="s">"token"</span><span class="p">,</span> <span class="s">"t"</span><span class="p">,</span> <span class="s">""</span><span class="p">,</span> <span class="s">"access token of codic.jp"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">viper</span><span class="p">.</span><span class="nf">BindPFlag</span><span class="p">(</span><span class="s">"json"</span><span class="p">,</span> <span class="nx">rootCmd</span><span class="p">.</span><span class="nf">PersistentFlags</span><span class="p">().</span><span class="nf">Lookup</span><span class="p">(</span><span class="s">"json"</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="nx">viper</span><span class="p">.</span><span class="nf">BindPFlag</span><span class="p">(</span><span class="s">"token"</span><span class="p">,</span> <span class="nx">rootCmd</span><span class="p">.</span><span class="nf">PersistentFlags</span><span class="p">().</span><span class="nf">Lookup</span><span class="p">(</span><span class="s">"token"</span><span class="p">))</span>
</span></span></code></pre></div><p>こんな感じで <a href="https://github.com/spf13/cobra" title="spf13/cobra: A Commander for modern Go CLI interactions">spf13/cobra</a> 側のフラグ(厳密には <a href="https://github.com/spf13/pflag" title="spf13/pflag: Drop-in replacement for Go's flag package, implementing POSIX/GNU-style --flags.">spf13/pflag</a>)と <a href="https://github.com/spf13/viper" title="spf13/viper: Go configuration with fangs">spf13/viper</a> をバインドしてしまう。
これで <code>cmd</code> パッケージ側からはフラグ情報を透過的に扱える。
フラグ情報を取り出す場合には</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">jsonFlag</span> <span class="o">:=</span> <span class="nx">viper</span><span class="p">.</span><span class="nf">GetBool</span><span class="p">(</span><span class="s">"json"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nx">token</span> <span class="o">:=</span> <span class="nx">viper</span><span class="p">.</span><span class="nf">GetString</span><span class="p">(</span><span class="s">"token"</span><span class="p">)</span>
</span></span></code></pre></div><p>などとすればよい。
分かれば簡単。</p>
<h2>ところで</h2>
<p><a href="http://blog.codic.jp/">2016年4月からブログが更新されてない</a>けど,そのうちサービスが止まるなんてないよね?
<a href="https://twitter.com/codic_project">Twitter アカウントは生きてる</a>みたいだし。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="http://niisi.hatenablog.jp/entry/2016/08/17/171000">【codic】プログラマ必見!もう変数名や関数名に困らない!プログラマのためのネーミングツールを紹介 - プログラミング向上雑記</a></li>
<li><a href="https://nelog.jp/codic">関数や変数のネーミングに悩んだら「codic」に日本語名を入力するとある程度解決するかも</a></li>
<li><a href="https://qiita.com/shtnkgm/items/45b4cd274fa813d29539">よく使うcurlコマンドのオプションまとめ(12個) - Qiita</a></li>
</ul>