tweeeetyのぶろぐ的めも

アウトプットが少なかったダメな自分をアウトプット<br>\(^o^)/

【git】.gitignoreの書き方 - テンプレートを使うその3 - 任意のファイルを.gitignoreとして読み込む

はじめに

git管理化から特定のファイルなどを無視したければ.gitignoreに追加します。
しかもだいたいが適宜気づいたら、つどつど。

ただ、言語、フレームワーク、ツールなどのファイルは
新しいプロジェクトのたび x 適宜気づくたびに追加するのは面倒です。

そこで、
プロジェクトによらない.gitignoreしたいものはテンプレ化しておきたいですよね。
.gitignoreテンプレ化については以下を参考にしてみてください。

しかし、せっかくテンプレは手にいれたものの
vim使ってるの自分だけだしプロジェクトの.gitignoreを汚したくない…
そんなときのメモです。

やってみる

.gitignoreファイルはリポジトリ直下にある.gitignore以外にも
configのcore.excludesfileに任意のファイルを指定する事で無視してくれます。

なので、例えば$HOME/.gitignore_myというファイルを作り
そこに個人的なignoreリストを作成してconfigに登録しておくことで、
チーム共通のリポジトリ.gitignoreを汚さずにすみます

# 
$ cd `プロジェクトリポジトリ`

# ${プロジェクトディレクトリ}/.gitignoreの他に読み込むファイルを指定
$ git config --local core.excludesfile $HOME/.gitignore_my

# .git/configに追記されている
$ cat .git/config 
[core]
  ~ 省略 ~ 
  excludesfile = /Users/hoge/.gitignore_my
  ~ 省略 ~ 

こうしておけば
gitignore.iogiboので取得した自分だけが使ってるツールなどのgitignoreテンプレート設定を別にすることができます。

おわり

gitは長らく使ってるつもりでも知らない事が多いですね\(^o^)/

【git】.gitignoreの書き方 - テンプレートを使うその2 - gibo(gitignore boilerplates)

はじめに

git管理化から特定のファイルなどを無視したければ.gitignoreに追加します。
しかもだいたいが適宜気づいたら、つどつど。

ただ、言語、フレームワーク、ツールなどのファイルは
新しいプロジェクトのたび x 適宜気づくたびに追加するのは面倒です。

そこで、
今回はお決まりのgitignoreテンプレを提供してくれるgiboのメモ

アジェンダ

  1. giboとは
  2. giboを使ってみる for mac

1. giboとは

公式サイトはこちらです。 https://github.com/simonwhitaker/gibo

gibo (short for .gitignore boilerplates) is a shell script to help you easily access .gitignore boilerplates from github.com/github/gitignore.

公式からですが、giboはgithub.com/github/gitignoreのボイラープレートにアクセスしてくれるshell script とのことです。

むしろボイラープレートって言葉が初耳でした..w
ボイラープレートとは

2. giboを使ってみる for mac

使い方は簡単です。
macでhomebrewを使っているならbrew install giboとするだけです。

使ってみた感じです。

gibo入れる
# インストール
$ brew install gibo

# version確認してみる
# + 何か怒られるが、これは後述する-lすると.gitignore-boilerplatesが作られて解消されます
gibo --version
fatal: Cannot change to '/Users/hoge/.gitignore-boilerplates': No such file or directory
gibo 1.0.5 by Simon Whitaker <sw@netcetera.org>

# git -lでリスト確認
# + .gitignore-boilerplates の作成
$ gibo -l
fatal: Cannot change to '/Users/hoge/.gitignore-boilerplates': No such file or directory
Cloning https://github.com/github/gitignore.git to /Users/hoge/.gitignore-boilerplates
Cloning into '/Users/hoge/.gitignore-boilerplates'...
remote: Counting objects: 6896, done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 6896 (delta 6), reused 11 (delta 2), pack-reused 6880
Receiving objects: 100% (6896/6896), 1.37 MiB | 299.00 KiB/s, done.
Resolving deltas: 100% (3687/3687), done.
Checking connectivity... done.
=== Languages ===

Actionscript    CommonLisp    Erlang      IGORPro     Maven     Prestashop    Scrivener   Unity
Ada     Composer    ExpressionEngine  Java      Mercury     Processing    Sdcc      UnrealEngine
Agda      Concrete5   ExtJs     Jboss     MetaProgrammingSystem PureScript    SeamGen     VisualStudio
Android     Coq     Fancy     Jekyll      Nanoc     Python      SketchUp    VVVV
AppceleratorTitanium  CraftCMS    Finale      Joomla      Nim     Qooxdoo     Smalltalk   Waf
AppEngine   CUDA      ForceDotCom   Julia     Node      Qt      Stella      WordPress
ArchLinuxPackages D     Fortran     KiCad     Objective-C   R     SugarCRM    Xojo
Autotools   Dart      FuelPHP     Kohana      OCaml     Rails     Swift     Yeoman
C++     Delphi      Gcov      LabVIEW     Opa     RhodesRhomobile   Symfony     Yii
C     DM      GitBook     Laravel     OpenCart    ROS     SymphonyCMS   ZendFramework
CakePHP     Drupal      Go      Leiningen   OracleForms   Ruby      Terraform   Zephir
CFWheels    Eagle     Gradle      LemonStand    Packer      Rust      TeX
ChefCookbook    Elisp     Grails      Lilypond    Perl      Sass      Textpattern
Clojure     Elixir      GWT     Lithium     Phalcon     Scala     TurboGears2
CMake     Elm     Haskell     Lua     PlayFramework   Scheme      Typo3
CodeIgniter   EPiServer   Idris     Magento     Plone     SCons     Umbraco

=== Global ===

Anjuta      CVS     Espresso    Lazarus     ModelSim    Redis     TextMate    Xcode
Ansible     DartEditor    FlexBuilder   LibreOffice   Momentics   SBT     TortoiseGit   XilinxISE
Archives    Dreamweaver   GPG     Linux     MonoDevelop   SlickEdit   Vagrant
Bazaar      Dropbox     JDeveloper    LyX     NetBeans    Stata     Vim
BricxCC     Eclipse     JEnv      macOS     Ninja     SublimeText   VirtualEnv
Calabash    EiffelStudio    JetBrains   Matlab      NotepadPP   SVN     VisualStudioCode
Cloud9      Emacs     Kate      Mercurial   Otto      SynopsysVCS   WebMethods
CodeKit     Ensime      KDevelop4   MicrosoftOffice   Redcar      Tags      Windows
[~ 15:44:06]$ gibo --version
gibo 1.0.5 by Simon Whitaker <sw@netcetera.org>
https://github.com/simonwhitaker/gibo

# 再度versionを確認
$ gibo --version
gibo 1.0.5 by Simon Whitaker <sw@netcetera.org>
https://github.com/simonwhitaker/gibo
gibo使う

vim用のgitignoreを出力してみる

$ gibo vim
### https://raw.github.com/github/gitignore/8edb8a95c4c4b3dce71a378aaaf89275510b9cef/Global/vim.gitignore

# Swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-v][a-z]
[._]sw[a-p]

# Session
Session.vim

# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
その他

見ての通り標準出力に出力されるだけなので自分で追記してやります。

$ gibo vim >> .gitignore

また、適宜updateして更新してあげましょう。

$ gibo -u

おわり

こういうテンプレは自分でメンテしてくのも大変だし
お任せできるって素敵ですね\(^o^)/

【git】.gitignoreの書き方 - テンプレートを使うその1 - gitignore.io

はじめに

git管理化から特定のファイルなどを無視したければ.gitignoreに追加します。
しかもだいたいが適宜気づいたら、つどつど。

ただ、言語、フレームワーク、ツールなどのファイルは
新しいプロジェクトのたび x 適宜気づくたびに追加するのは面倒です。

そこで、
今回はお決まりのgitignoreテンプレを提供してくれるgitignore.ioのメモ f:id:tweeeety:20170622002147p:plain

アジェンダ

  1. gitignore.ioとは
  2. gitignore.ioをcurlで使ってみる
  3. gitignore.ioをbashのコマンドにする
  4. gitignore.ioをgit aliasにしてgit ignore xxxで使えるようにする

1. gitignore.ioとは

公式サイトはこちらです。 https://www.gitignore.io/

Create useful .gitignore files for your project

公式ページからの引用ですが、有用な.gitignoreを作ってくれる君ですね。

また、テンプレ集という意味だと
https://github.com/github/gitignore もあるので見てみると良いかもしれないです。

2. gitignore.ioをcurlで使ってみる

使い方は簡単。
https://www.gitignore.io/api/${言語やツール} と指定するだけです。

ためしにvim用のgitignore

$ curl -L -s https://www.gitignore.io/api/vim

# Created by https://www.gitignore.io/api/vim

### Vim ###
# swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-v][a-z]
[._]sw[a-p]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags

# End of https://www.gitignore.io/api/vim

標準出力に出力されるので、そのまま.gitignoreを作るなり追記するなりでも良さそうです。

# 追記
curl -L -s https://www.gitignore.io/api/vim >> .gitignore

また、同時に複数指定することもできます。

$ curl -L -s https://www.gitignore.io/api/vim,java

# Created by https://www.gitignore.io/api/vim,java

### Java ###
# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

### Vim ###
# swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-v][a-z]
[._]sw[a-p]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags

# End of https://www.gitignore.io/api/vim,java

3. gitignore.ioをbashのコマンドにする

公式のまんまですが下のようにbashのfunctionにしてしまいます。

echo "function gi() { curl -L -s https://www.gitignore.io/api/\$@ ;}" >> ~/.bash_profile && source ~/.bash_profile

使ってみる

# bashの設定に追加
$ echo "function gi() { curl -L -s https://www.gitignore.io/api/\$@ ;}" >> ~/.bash_profile && source ~/.bash_profile

# 打ってみる
$ gi vim

# Created by https://www.gitignore.io/api/vim

### Vim ###
# swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-v][a-z]
[._]sw[a-p]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags

# End of https://www.gitignore.io/api/vim

4. gitignore.ioをgit aliasにしてgit ignore xxxで使えるようにする

bash的なコマンドも良いですが、どうせならgitコマンドぽく使いたいですよね。

こちらも公式のまんまですが、
以下のようにするとgitのsubコマンドのように使えます。

git config --global alias.ignore '!gi() { curl -L -s https://www.gitignore.io/api/$@ ;}; gi'

使ってみる

# aliasにする前
# 当然怒られる
$ git ignore vim
git: 'ignore' is not a git command. See 'git --help'.

# aliasにする
$ git config --global alias.ignore '!gi() { curl -L -s https://www.gitignore.io/api/$@ ;}; gi'

# どや!
$ git ignore vim

# Created by https://www.gitignore.io/api/vim

### Vim ###
# swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-v][a-z]
[._]sw[a-p]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags

# End of https://www.gitignore.io/api/vim

おわり

こういうテンプレは自分でメンテしてくのも大変だし
お任せできるって素敵ですね\(^o^)/

【go】funcの引数にstructの値渡し、ポインタ渡ししたときの挙動メモ

はじめに

goをなんとなく書き始めて半年経っていないくらいですが、
Goでxxxのポインタを取っているプログラムはだいたい全部間違っている という記事を見てなるほどなーと思いました。

最初のころとか確かに何となく無駄にポインタで渡していた気がする…

と、思ったところで、structについても自分で再確認するかーと思ったので
funcの引数にstructの値渡しとポインタ渡しで挙動を確かめてみるだけのgo初心者メモ

ソース

structを値として渡す場合とポインタとして渡す場合のいくつかだけの単純サンプル

package main

import "log"

type Data struct {
  Name string
  Age  int64
}

// #1.
//   arg :値渡し
//   処理:値のままプロパティアクセスして代入
func changeNameArgNormalAccessNormal(d Data) {
  d.Name = "fuga"
}

// #2.
//   arg :値渡し
//   処理:ポインタにしてからプロパティアクセスして代入
func changeNameArgNormalAccessPointer(d Data) {
  dPointer := &d
  dPointer.Name = "fuga"
}

// #3.
//   arg :ポインタ渡し
//   処理:値にしてからプロパティアクセスして代入
func changeNameArgPointerAccessNormal(dPointer *Data) {
  d := *dPointer
  d.Name = "fuga"
}

// #4.
//   arg :ポインタ渡し
//   処理:ポインタのままプロパティアクセスして代入
func changeNameArgPointerAccessPointer(dPointer *Data) {
  dPointer.Name = "fuga"
}

func main() {
  // #1
  d1 := Data{Name: "hoge", Age: 20}
  changeNameArgNormalAccessNormal(d1)
  log.Printf("d1: %+v", d1)

  // #2
  d2 := Data{Name: "hoge", Age: 20}
  changeNameArgNormalAccessPointer(d2)
  log.Printf("d2: %+v", d2)

  // #3
  d3 := Data{Name: "hoge", Age: 20}
  changeNameArgPointerAccessNormal(&d3)
  log.Printf("d3: %+v", d3)

  // #4
  d4 := Data{Name: "hoge", Age: 20}
  changeNameArgPointerAccessPointer(&d4)
  log.Printf("d4: %+v", d4)
}

https://github.com/tweeeety/go-struct-ref-sample

結果

#4 だけが元のコードの結果も変わります。
まぁ、そうだよね、というくらいでした。

$ go run struct_ref.go 
2017/06/21 23:53:22 d1: {Name:hoge Age:20}
2017/06/21 23:53:22 d2: {Name:hoge Age:20}
2017/06/21 23:53:22 d3: {Name:hoge Age:20}
2017/06/21 23:53:22 d4: {Name:fuga Age:20}

おわり

最初のころmapあたりは無駄にポインタにしたことがある気もしなくもない\(^o^)/

【go】golangのCLIパッケージ(urfave/cli)を使ってみるメモ

はじめに

command line cliなscriptを作りたいとき、
たいていどの言語でも引数を扱うライブラリがありますよね。

golangではとても便利なurfave/cliというパッケージがあります。
(以前は github.com/codegangsta/cli というリポジトリでした)

この使い方を簡単にメモ

アジェンダ

  1. getting start的な
  2. Arguments とか
  3. Flags 使ってみる
  4. Subcommands でいろいろ

その前に

この記事に使用したソースです。
https://github.com/tweeeety/go-command-line-sample/tree/master/src/script

1. getting start的な

install

go getするだけです。
glide使ってればglide installするだけですね。

$ go get github.com/urfave/cli

使ってみる

前提

これ以降の記述は、すべて以下のようなdir構成になっているものとして進めます。
今回の記事ようにgo-command-line-sampleというプロジェクト ディレクトリを作ってます。

# 作成したgo-command-line-sample
$ pwd
パス/go-command-line-sample

$ tree -L 3 ../go-command-line-sample
../go-command-line-sample
└── src
    └── script # この配下にscriptごとにdirを切る
        ├── glide.lock
        ├── glide.yaml
        └── vendor
簡単に試す

まずはprintするだけ、help出すだけ。
公式と同じですね

src/script/cli_10/main.go
package main

import (
  "10t"
  "os"

  "github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  app.Name = "boom"
  app.Usage = "make an explosive entrance"

  app.Action = func(c *cli.Context) error {
    fmt.Println("boom! I say!")
    return nil
  }

  app.Run(os.Args)
}
実行
# 叩いてみる
$ go run src/script/cli_10/main.go 
boom! I say!

# helpってみる
$ go run src/script/cli_10/main.go help
NAME:
   boom - make an explosive entrance

USAGE:
   main [global options] command [command options] [arguments...]

VERSION:
   0.0.0

COMMANDS:
     help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --help, -h     show help
   --version, -v  print the version
go install

おもむろにgo installしてみます。

# scriptがいる場所まで移動
$ cd src/script/cli_10

# install
$ go instal

# プロジェクト直下にもどる
$ cd パス/go-command-line-sample

# binにcli_10が出来てる
$ tree -L 3
.
├── bin
│   └── cli_10
├── pkg
│   └── darwin_amd64
│       └── script
└── src
    └── script
        ├── cli_10
        ├── glide.lock
        ├── glide.yaml
        └── vendor

# たたいてみる
$ ./bin/cli_10
boom! I say!

ここまで動かすのもめっちゃ簡単ですね!

2. Arguments とか

app := cli.NewApp() した後の基本的な使い方です。

src/script/cli_20/main.go
package main

import (
  "fmt"
  "os"

  "github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  app.Name = "cli_20"
  app.Usage = "cli_20 sample"
  app.Version = "1.2.1"

  // before
  app.Before = func(c *cli.Context) error {
    fmt.Println("-- Before --")
    return nil
  }

  // action
  app.Action = func(c *cli.Context) error {
    fmt.Println("-- Action --")

    fmt.Printf("c.NArg()        : %+v\n", c.NArg())
    fmt.Printf("c.Args()        : %+v\n", c.Args())
    fmt.Printf("c.Args().Get(0) : %+v\n", c.Args().Get(0))
    fmt.Printf("c.Args()[0]     : %+v\n", c.Args()[0])
    fmt.Printf("c.FlagNames     : %+v\n", c.FlagNames())

    // Help表示
    //cli.ShowAppHelp(c) 

    // version表示
    cli.ShowVersion(c) 

    return nil
  }

  // after
  app.After = func(c *cli.Context) error {
    fmt.Println("-- After --")
    return nil
  }

  // true: go run app.go helpと打ってもhelpが出なくなる
  app.HideHelp = true

  app.Run(os.Args)
}
実行
# そのまま実行
$ go run src/script/cli_20/main.go hoge fuga piyo
-- Before --
-- Action --
c.NArg()        : 3
c.Args()        : [hoge fuga piyo]
c.Args().Get(0) : hoge
c.Args()[0]     : hoge
c.FlagNames     : []
cli_20 version 1.2.1
-- After --

# help
$ go run main.go help
-- Before --
NAME:
   cli_20 - cli_20 sample

USAGE:
   main [global options] command [command options] [arguments...]

VERSION:
   1.2.1

COMMANDS:
     help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --help, -h     show help
   --version, -v  print the version
-- After --


# `app.HideHelp = true` なのでhelpは引数として見なされる
$ go run src/script/cli_20/main.go help
-- Before --
-- Action --
c.NArg()        : 1
c.Args()        : [help]
c.Args().Get(0) : help
c.Args()[0]     : help
c.String()      : 
cli_20 version 1.2.1
-- After --
context

c.XXXXで取れるcontextについてはこの辺を見るとかなりたくさんありますね。
https://godoc.org/github.com/urfave/cli#Context

3. Flags 使ってみる

flagsは-lang english-l english的なオプションを受け取るためのものです。
ショートオプションや省略時のdefault optionも指定できます。

command、option、flagとは?というところは以下が大変参考になります。
Go言語によるCLIツール開発とUNIX哲学について

使ってみる

src/script/cli_30/main.go
package main

import (
  "fmt"
  "os"

  "github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  app.Name = "cli_30"
  app.Usage = "cli_30 sample"
  app.Version = "1.2.1"

  os.Setenv("SAMPLE_ENV", "sample env dayo")

  // flags
  app.Flags = []cli.Flag{
    cli.StringFlag{
      Name:  "lang, l",
      Value: "english",
      Usage: "language for the greeting",
    },
    cli.StringFlag{
      Name:  "meridian, m",
      Value: "AM",
      Usage: "meridian for the greeting",
    },
    cli.StringFlag{
      Name:  "time, t",
      Value: "07:00",
      // ``で囲むとhelp時のPlaceholderとしても使える
      // https://github.com/urfave/cli#placeholder-values
      Usage: "`your time` for the greeting",
    },
    cli.StringFlag{
      Name:  "aaa, a",
      Value: "sample",
      // default値をValueからではなくEnvから取る
      EnvVar: "SAMPLE_ENV",
    },
  }

  // action
  app.Action = func(c *cli.Context) error {
    fmt.Println("-- Action --")

    fmt.Printf("c.GlobalFlagNames() : %+v\n", c.GlobalFlagNames())
    fmt.Printf("c.String(\"lang\")    : %+v\n", c.String("lang"))
    fmt.Printf("c.String(\"m\")       : %+v\n", c.String("m"))
    fmt.Printf("c.String(\"time\")    : %+v\n", c.String("time"))
    fmt.Printf("c.String(\"a\")       : %+v\n", c.String("a"))

    return nil
  }

  app.Run(os.Args)
}
実行
# 実行
# 引数のkey:valueはスペースでもイコールでもいける
$ go run src/script/cli_30/main.go -l hoge -m=PM
-- Action --
c.GlobalFlagNames() : [aaa lang meridian time]
c.String("lang")    : hoge
c.String("m")       : PM
c.String("time")    : 07:00
c.String("a")       : sample env dayo

# help
$ go run src/script/cli_30/main.go -h
NAME:
   cli_30 - cli_30 sample

USAGE:
   main [global options] command [command options] [arguments...]

VERSION:
   1.2.1

COMMANDS:
     help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --aaa value, -a value           (default: "sample") [$SAMPLE_ENV]
   --lang value, -l value          language for the greeting (default: "english")
   --meridian value, -m value      meridian for the greeting (default: "AM")
   --time your time, -t your time  your time for the greeting (default: "07:00")
   --help, -h                      show help
   --version, -v                   print the version

Flagの種類

Flagとして設定できる種類がいくつかあるのでその例です。
StringFlagBoolFlagが指定できます。

src/script/cli_31/main.go
package main

import (
  "fmt"
  "os"

  "github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  app.Name = "cli_31"
  app.Usage = "cli_31 sample"
  app.Version = "1.2.1"

  // flags
  app.Flags = []cli.Flag{
    // StringFlag
    cli.StringFlag{
      Name:  "name, n",
      Value: "tarou",
      Usage: "your name",
    },
    // BoolFlag
    cli.BoolFlag{
      Name:  "gay, g",
      Usage: "are you gay boy?",
    },
  }

  // action
  app.Action = func(c *cli.Context) error {
    fmt.Println("-- Action --")
    fmt.Printf("c.GlobalFlagNames() : %+v\n", c.GlobalFlagNames())
    fmt.Printf("c.String(\"name\")    : %+v\n", c.String("name"))
    fmt.Printf("c.String(\"g\")       : %+v\n", c.Bool("g"))

    return nil
  }

  app.Run(os.Args)
}

実行
# 指定無しで叩いてみる
$ go run src/script/cli_31/main.go 
-- Action --
c.GlobalFlagNames() : [name gay]
c.String("name")    : tarou
c.String("g")       : false

# 指定有りで叩いてみる
# BoolFlagは、オプションを指定することでtrueとなるFlag
$ go run src/script/cli_31/main.go --name hoge -g
-- Action --
c.GlobalFlagNames() : [name gay]
c.String("name")    : hoge
c.String("g")       : true

4. Subcommands でいろいろ

公式の通りですが、
git-like のようなコマンドが設定できます。

普通に使ってみる

まずは公式のお試し

src/script/cli_40/main.go
package main

import (
  "fmt"
  "os"

  "github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  app.Name = "cli_40"
  app.Usage = "cli_40 sample"

  // command action
  // これまでのActionとは違い、flagsごとの挙動(Action)が設定できる
  app.Commands = []cli.Command{
    // `go run cli_40/main.go add パラメータ`でactionするコマンド
    {
      Name:    "add",
      Aliases: []string{"a"},
      Usage:   "add a task to the list",
      Action: func(c *cli.Context) error {
        fmt.Println("added task: ", c.Args().First())
        return nil
      },
    },
    // `go run cli_40/main.go complete パラメータ`でactionするコマンド
    {
      Name:    "complete",
      Aliases: []string{"c"},
      Usage:   "complete a task on the list",
      Action: func(c *cli.Context) error {
        fmt.Println("completed task: ", c.Args().First())
        return nil
      },
    },
    {
      Name:    "template",
      Aliases: []string{"t"},
      Usage:   "options for task templates",
      Subcommands: []cli.Command{
        // `go run cli_40/main.go template add パラメータ`でactionするコマンド
        {
          Name:  "add",
          Usage: "add a new template",
          Action: func(c *cli.Context) error {
            fmt.Println("new task template: ", c.Args().First())
            return nil
          },
        },
        // `go run cli_40/main.go template remove パラメータ`でactionするコマンド
        {
          Name:  "remove",
          Usage: "remove an existing template",
          Action: func(c *cli.Context) error {
            fmt.Println("removed task template: ", c.Args().First())
            return nil
          },
        },
      },
    },
  }

  app.Run(os.Args)
}

実行

実行してみるとこんな感じ

# addコマンドとパラメータで実行
$ go run src/script/cli_40/main.go t add hoge
added task:  hoge

# t(template)コマンドとaddサブコマンドで実行
$ go run src/script/cli_40/main.go t t add hoge
new task template:  hoge

$ go run src/script/cli_40/main.go -h
NAME:
   cli_04 - cli_04 sample

USAGE:
   main [global options] command [command options] [arguments...]

VERSION:
   0.0.0

COMMANDS:
     add, a       add a task to the list
     complete, c  complete a task on the list
     template, t  options for task templates
     help, h      Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --help, -h     show help
   --version, -v  print the version

COMMANDStemplateのSubcommandsが表示されないじゃないかーと思いますが
templateまで打って-hすればちゃんと出ます。

かしこい!

$ go run cli_40/main.go t -h
NAME:
   cli_04 template - options for task templates

USAGE:
   cli_04 template command [command options] [arguments...]

COMMANDS:
     add     add a new template
     remove  remove an existing template

OPTIONS:
   --help, -h  show help

before/action/afterとの関係を見る

COMMANDS(のAction)とbefore/action/after らへんはどういう関係で動くか。

src/script/cli_41/main.go
import (
  "fmt"
  "os"

  "github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  app.Name = "cli_41"
  app.Usage = "cli_41 sample"

  // before
  app.Before = func(c *cli.Context) error {
    fmt.Println("-- Before --")
    return nil
  }

  // command action
  app.Commands = []cli.Command{
    // `go run cli_04/main.go sample パラメータ`でactionするコマンド
    {
      Name:    "sample",
      Aliases: []string{"s"},
      Usage:   "sample task",
      Action: func(c *cli.Context) error {
        fmt.Println("-- Sample Action --")
        fmt.Println("sample task: ", c.Args().First())
        return nil
      },
    },
  }

  // action
  app.Action = func(c *cli.Context) error {
    fmt.Println("-- Nomal Action --")
    return nil
  }

  // after
  app.After = func(c *cli.Context) error {
    fmt.Println("-- After --")
    return nil
  }
  app.Run(os.Args)
}
実行

COMMANDSが渡されたときはapp.Actionは実行されず、
COMMANDSが何も渡されないとapp.Actionが実行されます。

$ go run src/script/cli_41/main.go sample hoge
-- Before --
-- Sample Action --
sample task:  hoge
-- After --

$ go run src/script/cli_41/main.go
-- Before --
-- Nomal Action --
-- After --

c.Commandを見てみる

COMMANDS に指定したAction: func内で使えるc.Commandを見てみます。
こんなのが使えるよー程度ですね。

src/script/cli_42/main.go
package main

import (
  "fmt"
  "os"

  "github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  app.Name = "cli_42"
  app.Usage = "cli_42 sample"

  // command action
  app.Commands = []cli.Command{
    {
      Name:    "sample",
      Aliases: []string{"s"},
      Usage:   "sample task",
      Action: func(c *cli.Context) error {
        fmt.Println("-- Sample Action --")

        fmt.Printf("c.Command.FullName()        : %+v\n", c.Command.FullName())
        fmt.Printf("c.Command.HasName(\"sample\") : %+v\n", c.Command.HasName("sample"))
        fmt.Printf("c.Command.Names()           : %+v\n", c.Command.Names())
        fmt.Printf("c.Command.VisibleFlags()    : %+v\n", c.Command.VisibleFlags())

        return nil
      },
    },
  }

  app.Run(os.Args)
}
実行
$ go run src/script/cli_42/main.go sample hoge
-- Sample Action --
c.Command.FullName()        : sample
c.Command.HasName("sample") : true
c.Command.Names()           : [sample s]
c.Command.VisibleFlags()    : [--help, -h show help]

Subcommands x Flags

次はSubcommands x Flagsの例です。
Flags単独の例は前述してますが、各Subcommandsごとにも設定できます。

src/script/cli_43/main.go
import (
  "fmt"
  "os"

  "github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  app.Name = "cli_43"
  app.Usage = "cli_43 sample"

  // command action
  app.Commands = []cli.Command{
    // addコマンドに対してFlagsを設定
    {
      Name:    "add",
      Aliases: []string{"a"},
      Usage:   "add a task to the list",
      Action: func(c *cli.Context) error {
        fmt.Println("added task: ", c.Args().First())
        fmt.Println("json      : ", c.String("j"))
        fmt.Println("exec      : ", c.String("e"))
        return nil
      },
      Flags: []cli.Flag{
        cli.StringFlag{
          Name:  "json, j",
          Value: "add.json",
        },
        cli.BoolFlag{
          Name: "exec, e",
        },
      },
    },
    // completeコマンドに対してFlagsを設定
    {
      Name:    "complete",
      Aliases: []string{"c"},
      Usage:   "complete a task on the list",
      Action: func(c *cli.Context) error {
        fmt.Println("completed task: ", c.Args().First())
        fmt.Println("csv           : ", c.String("c"))
        fmt.Println("exec          : ", c.String("e"))
        return nil
      },
      Flags: []cli.Flag{
        cli.StringFlag{
          Name:  "csv, c",
          Value: "add.json",
        },
        cli.BoolFlag{
          Name: "exec, e",
        },
      },
    },
  }
  app.Run(os.Args)
}
実行
# addコマンドを実行。addコマンドに指定したFlagsを指定
$ go run src/script/cli_43/main.go add -j test.json -e
added task:  
json      :  test.json
exec      :  true

# addコマンドを実行。completeコマンドに指定したFlagsを指定
# オプションが違うよ、という事でhelpが出る
$ go run src/script/cli_43/main.go add -c test.csv -e
Incorrect Usage: flag provided but not defined: -c

NAME:
   main add - add a task to the list

USAGE:
   main add [command options] [arguments...]

OPTIONS:
   --json value, -j value  (default: "add.json")
   --exec, -e              

# completeコマンドを実行。addコマンドに指定したFlagsを指定
$ go run src/script/cli_43/main.go complete -c test.csv
completed task:  
csv           :  test.csv
exec          :  false

おわり

urfave/cli いろいろてんこもりで便利ですね。
ここまでがっつり機能いらないよって場合は、標準のflag パッケージの方を使ってサクっと作るのも良さそうです
\(^o^)/

【go】golangのCLIパッケージ(urfave/cli)を使ってみるメモ

はじめに

command line cliなscriptを作りたいとき、
たいていどの言語でも引数を扱うライブラリがありますよね。

golangではとても便利なurfave/cliというパッケージがあります。
(以前は github.com/codegangsta/cli というリポジトリでした)

この使い方を簡単にメモ

アジェンダ

  1. getting start的な
  2. Arguments とか
  3. Flags 使ってみる
  4. Subcommands でいろいろ

その前に

この記事に使用したソースです。
https://github.com/tweeeety/go-command-line-sample/tree/master/src/script

1. getting start的な

install

go getするだけです。
glide使ってればglide installするだけですね。

$ go get github.com/urfave/cli

使ってみる

前提

これ以降の記述は、すべて以下のようなdir構成になっているものとして進めます。
今回の記事ようにgo-command-line-sampleというプロジェクト ディレクトリを作ってます。

# 作成したgo-command-line-sample
$ pwd
パス/go-command-line-sample

$ tree -L 3 ../go-command-line-sample
../go-command-line-sample
└── src
    └── script # この配下にscriptごとにdirを切る
        ├── glide.lock
        ├── glide.yaml
        └── vendor
簡単に試す

まずはprintするだけ、help出すだけ。
公式と同じですね

src/script/cli_10/main.go
package main

import (
  "10t"
  "os"

  "github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  app.Name = "boom"
  app.Usage = "make an explosive entrance"

  app.Action = func(c *cli.Context) error {
    fmt.Println("boom! I say!")
    return nil
  }

  app.Run(os.Args)
}
実行
# 叩いてみる
$ go run src/script/cli_10/main.go 
boom! I say!

# helpってみる
$ go run src/script/cli_10/main.go help
NAME:
   boom - make an explosive entrance

USAGE:
   main [global options] command [command options] [arguments...]

VERSION:
   0.0.0

COMMANDS:
     help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --help, -h     show help
   --version, -v  print the version
go install

おもむろにgo installしてみます。

# scriptがいる場所まで移動
$ cd src/script/cli_10

# install
$ go instal

# プロジェクト直下にもどる
$ cd パス/go-command-line-sample

# binにcli_10が出来てる
$ tree -L 3
.
├── bin
│   └── cli_10
├── pkg
│   └── darwin_amd64
│       └── script
└── src
    └── script
        ├── cli_10
        ├── glide.lock
        ├── glide.yaml
        └── vendor

# たたいてみる
$ ./bin/cli_10
boom! I say!

ここまで動かすのもめっちゃ簡単ですね!

2. Arguments とか

app := cli.NewApp() した後の基本的な使い方です。

src/script/cli_20/main.go
package main

import (
  "fmt"
  "os"

  "github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  app.Name = "cli_20"
  app.Usage = "cli_20 sample"
  app.Version = "1.2.1"

  // before
  app.Before = func(c *cli.Context) error {
    fmt.Println("-- Before --")
    return nil
  }

  // action
  app.Action = func(c *cli.Context) error {
    fmt.Println("-- Action --")

    fmt.Printf("c.NArg()        : %+v\n", c.NArg())
    fmt.Printf("c.Args()        : %+v\n", c.Args())
    fmt.Printf("c.Args().Get(0) : %+v\n", c.Args().Get(0))
    fmt.Printf("c.Args()[0]     : %+v\n", c.Args()[0])
    fmt.Printf("c.FlagNames     : %+v\n", c.FlagNames())

    // Help表示
    //cli.ShowAppHelp(c) 

    // version表示
    cli.ShowVersion(c) 

    return nil
  }

  // after
  app.After = func(c *cli.Context) error {
    fmt.Println("-- After --")
    return nil
  }

  // true: go run app.go helpと打ってもhelpが出なくなる
  app.HideHelp = true

  app.Run(os.Args)
}
実行
# そのまま実行
$ go run src/script/cli_20/main.go hoge fuga piyo
-- Before --
-- Action --
c.NArg()        : 3
c.Args()        : [hoge fuga piyo]
c.Args().Get(0) : hoge
c.Args()[0]     : hoge
c.FlagNames     : []
cli_20 version 1.2.1
-- After --

# help
$ go run main.go help
-- Before --
NAME:
   cli_20 - cli_20 sample

USAGE:
   main [global options] command [command options] [arguments...]

VERSION:
   1.2.1

COMMANDS:
     help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --help, -h     show help
   --version, -v  print the version
-- After --


# `app.HideHelp = true` なのでhelpは引数として見なされる
$ go run src/script/cli_20/main.go help
-- Before --
-- Action --
c.NArg()        : 1
c.Args()        : [help]
c.Args().Get(0) : help
c.Args()[0]     : help
c.String()      : 
cli_20 version 1.2.1
-- After --
context

c.XXXXで取れるcontextについてはこの辺を見るとかなりたくさんありますね。
https://godoc.org/github.com/urfave/cli#Context

3. Flags 使ってみる

flagsは-lang english-l english的なオプションを受け取るためのものです。
ショートオプションや省略時のdefault optionも指定できます。

一瞬argumentsと混同しましたが、

使ってみる

src/script/cli_30/main.go
package main

import (
  "fmt"
  "os"

  "github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  app.Name = "cli_30"
  app.Usage = "cli_30 sample"
  app.Version = "1.2.1"

  os.Setenv("SAMPLE_ENV", "sample env dayo")

  // flags
  app.Flags = []cli.Flag{
    cli.StringFlag{
      Name:  "lang, l",
      Value: "english",
      Usage: "language for the greeting",
    },
    cli.StringFlag{
      Name:  "meridian, m",
      Value: "AM",
      Usage: "meridian for the greeting",
    },
    cli.StringFlag{
      Name:  "time, t",
      Value: "07:00",
      // ``で囲むとhelp時のPlaceholderとしても使える
      // https://github.com/urfave/cli#placeholder-values
      Usage: "`your time` for the greeting",
    },
    cli.StringFlag{
      Name:  "aaa, a",
      Value: "sample",
      // default値をValueからではなくEnvから取る
      EnvVar: "SAMPLE_ENV",
    },
  }

  // action
  app.Action = func(c *cli.Context) error {
    fmt.Println("-- Action --")

    fmt.Printf("c.GlobalFlagNames() : %+v\n", c.GlobalFlagNames())
    fmt.Printf("c.String(\"lang\")    : %+v\n", c.String("lang"))
    fmt.Printf("c.String(\"m\")       : %+v\n", c.String("m"))
    fmt.Printf("c.String(\"time\")    : %+v\n", c.String("time"))
    fmt.Printf("c.String(\"a\")       : %+v\n", c.String("a"))

    return nil
  }

  app.Run(os.Args)
}
実行
# 実行
# 引数のkey:valueはスペースでもイコールでもいける
$ go run src/script/cli_30/main.go -l hoge -m=PM
-- Action --
c.GlobalFlagNames() : [aaa lang meridian time]
c.String("lang")    : hoge
c.String("m")       : PM
c.String("time")    : 07:00
c.String("a")       : sample env dayo

# help
$ go run src/script/cli_30/main.go -h
NAME:
   cli_30 - cli_30 sample

USAGE:
   main [global options] command [command options] [arguments...]

VERSION:
   1.2.1

COMMANDS:
     help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --aaa value, -a value           (default: "sample") [$SAMPLE_ENV]
   --lang value, -l value          language for the greeting (default: "english")
   --meridian value, -m value      meridian for the greeting (default: "AM")
   --time your time, -t your time  your time for the greeting (default: "07:00")
   --help, -h                      show help
   --version, -v                   print the version

Flagの種類

Flagとして設定できる種類がいくつかあるのでその例です。
StringFlagBoolFlagが指定できます。

src/script/cli_31/main.go
package main

import (
  "fmt"
  "os"

  "github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  app.Name = "cli_31"
  app.Usage = "cli_31 sample"
  app.Version = "1.2.1"

  // flags
  app.Flags = []cli.Flag{
    // StringFlag
    cli.StringFlag{
      Name:  "name, n",
      Value: "tarou",
      Usage: "your name",
    },
    // BoolFlag
    cli.BoolFlag{
      Name:  "gay, g",
      Usage: "are you gay boy?",
    },
  }

  // action
  app.Action = func(c *cli.Context) error {
    fmt.Println("-- Action --")
    fmt.Printf("c.GlobalFlagNames() : %+v\n", c.GlobalFlagNames())
    fmt.Printf("c.String(\"name\")    : %+v\n", c.String("name"))
    fmt.Printf("c.String(\"g\")       : %+v\n", c.Bool("g"))

    return nil
  }

  app.Run(os.Args)
}

実行
# 指定無しで叩いてみる
$ go run src/script/cli_31/main.go 
-- Action --
c.GlobalFlagNames() : [name gay]
c.String("name")    : tarou
c.String("g")       : false

# 指定有りで叩いてみる
# BoolFlagは、オプションを指定することでtrueとなるFlag
$ go run src/script/cli_31/main.go --name hoge -g
-- Action --
c.GlobalFlagNames() : [name gay]
c.String("name")    : hoge
c.String("g")       : true

4. Subcommands でいろいろ

公式の通りですが、
git-like のようなコマンドが設定できます。

普通に使ってみる

まずは公式のお試し

src/script/cli_40/main.go
package main

import (
  "fmt"
  "os"

  "github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  app.Name = "cli_40"
  app.Usage = "cli_40 sample"

  // command action
  // これまでのActionとは違い、flagsごとの挙動(Action)が設定できる
  app.Commands = []cli.Command{
    // `go run cli_40/main.go add パラメータ`でactionするコマンド
    {
      Name:    "add",
      Aliases: []string{"a"},
      Usage:   "add a task to the list",
      Action: func(c *cli.Context) error {
        fmt.Println("added task: ", c.Args().First())
        return nil
      },
    },
    // `go run cli_40/main.go complete パラメータ`でactionするコマンド
    {
      Name:    "complete",
      Aliases: []string{"c"},
      Usage:   "complete a task on the list",
      Action: func(c *cli.Context) error {
        fmt.Println("completed task: ", c.Args().First())
        return nil
      },
    },
    {
      Name:    "template",
      Aliases: []string{"t"},
      Usage:   "options for task templates",
      Subcommands: []cli.Command{
        // `go run cli_40/main.go template add パラメータ`でactionするコマンド
        {
          Name:  "add",
          Usage: "add a new template",
          Action: func(c *cli.Context) error {
            fmt.Println("new task template: ", c.Args().First())
            return nil
          },
        },
        // `go run cli_40/main.go template remove パラメータ`でactionするコマンド
        {
          Name:  "remove",
          Usage: "remove an existing template",
          Action: func(c *cli.Context) error {
            fmt.Println("removed task template: ", c.Args().First())
            return nil
          },
        },
      },
    },
  }

  app.Run(os.Args)
}

実行

実行してみるとこんな感じ

# addコマンドとパラメータで実行
$ go run src/script/cli_40/main.go t add hoge
added task:  hoge

# t(template)コマンドとaddサブコマンドで実行
$ go run src/script/cli_40/main.go t t add hoge
new task template:  hoge

$ go run src/script/cli_40/main.go -h
NAME:
   cli_04 - cli_04 sample

USAGE:
   main [global options] command [command options] [arguments...]

VERSION:
   0.0.0

COMMANDS:
     add, a       add a task to the list
     complete, c  complete a task on the list
     template, t  options for task templates
     help, h      Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --help, -h     show help
   --version, -v  print the version

COMMANDStemplateのSubcommandsが表示されないじゃないかーと思いますが
templateまで打って-hすればちゃんと出ます。

かしこい!

$ go run cli_40/main.go t -h
NAME:
   cli_04 template - options for task templates

USAGE:
   cli_04 template command [command options] [arguments...]

COMMANDS:
     add     add a new template
     remove  remove an existing template

OPTIONS:
   --help, -h  show help

before/action/afterとの関係を見る

COMMANDS(のAction)とbefore/action/after らへんはどういう関係で動くか。

src/script/cli_41/main.go
import (
  "fmt"
  "os"

  "github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  app.Name = "cli_41"
  app.Usage = "cli_41 sample"

  // before
  app.Before = func(c *cli.Context) error {
    fmt.Println("-- Before --")
    return nil
  }

  // command action
  app.Commands = []cli.Command{
    // `go run cli_04/main.go sample パラメータ`でactionするコマンド
    {
      Name:    "sample",
      Aliases: []string{"s"},
      Usage:   "sample task",
      Action: func(c *cli.Context) error {
        fmt.Println("-- Sample Action --")
        fmt.Println("sample task: ", c.Args().First())
        return nil
      },
    },
  }

  // action
  app.Action = func(c *cli.Context) error {
    fmt.Println("-- Nomal Action --")
    return nil
  }

  // after
  app.After = func(c *cli.Context) error {
    fmt.Println("-- After --")
    return nil
  }
  app.Run(os.Args)
}
実行

COMMANDSが渡されたときはapp.Actionは実行されず、
COMMANDSが何も渡されないとapp.Actionが実行されます。

$ go run src/script/cli_41/main.go sample hoge
-- Before --
-- Sample Action --
sample task:  hoge
-- After --

$ go run src/script/cli_41/main.go
-- Before --
-- Nomal Action --
-- After --

c.Commandを見てみる

COMMANDS に指定したAction: func内で使えるc.Commandを見てみます。
こんなのが使えるよー程度ですね。

src/script/cli_42/main.go
package main

import (
  "fmt"
  "os"

  "github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  app.Name = "cli_42"
  app.Usage = "cli_42 sample"

  // command action
  app.Commands = []cli.Command{
    {
      Name:    "sample",
      Aliases: []string{"s"},
      Usage:   "sample task",
      Action: func(c *cli.Context) error {
        fmt.Println("-- Sample Action --")

        fmt.Printf("c.Command.FullName()        : %+v\n", c.Command.FullName())
        fmt.Printf("c.Command.HasName(\"sample\") : %+v\n", c.Command.HasName("sample"))
        fmt.Printf("c.Command.Names()           : %+v\n", c.Command.Names())
        fmt.Printf("c.Command.VisibleFlags()    : %+v\n", c.Command.VisibleFlags())

        return nil
      },
    },
  }

  app.Run(os.Args)
}
実行
$ go run src/script/cli_42/main.go sample hoge
-- Sample Action --
c.Command.FullName()        : sample
c.Command.HasName("sample") : true
c.Command.Names()           : [sample s]
c.Command.VisibleFlags()    : [--help, -h show help]

Subcommands x Flags

次はSubcommands x Flagsの例です。
Flags単独の例は前述してますが、各Subcommandsごとにも設定できます。

src/script/cli_43/main.go
import (
  "fmt"
  "os"

  "github.com/urfave/cli"
)

func main() {
  app := cli.NewApp()
  app.Name = "cli_43"
  app.Usage = "cli_43 sample"

  // command action
  app.Commands = []cli.Command{
    // addコマンドに対してFlagsを設定
    {
      Name:    "add",
      Aliases: []string{"a"},
      Usage:   "add a task to the list",
      Action: func(c *cli.Context) error {
        fmt.Println("added task: ", c.Args().First())
        fmt.Println("json      : ", c.String("j"))
        fmt.Println("exec      : ", c.String("e"))
        return nil
      },
      Flags: []cli.Flag{
        cli.StringFlag{
          Name:  "json, j",
          Value: "add.json",
        },
        cli.BoolFlag{
          Name: "exec, e",
        },
      },
    },
    // completeコマンドに対してFlagsを設定
    {
      Name:    "complete",
      Aliases: []string{"c"},
      Usage:   "complete a task on the list",
      Action: func(c *cli.Context) error {
        fmt.Println("completed task: ", c.Args().First())
        fmt.Println("csv           : ", c.String("c"))
        fmt.Println("exec          : ", c.String("e"))
        return nil
      },
      Flags: []cli.Flag{
        cli.StringFlag{
          Name:  "csv, c",
          Value: "add.json",
        },
        cli.BoolFlag{
          Name: "exec, e",
        },
      },
    },
  }
  app.Run(os.Args)
}
実行
# addコマンドを実行。addコマンドに指定したFlagsを指定
$ go run src/script/cli_43/main.go add -j test.json -e
added task:  
json      :  test.json
exec      :  true

# addコマンドを実行。completeコマンドに指定したFlagsを指定
# オプションが違うよ、という事でhelpが出る
$ go run src/script/cli_43/main.go add -c test.csv -e
Incorrect Usage: flag provided but not defined: -c

NAME:
   main add - add a task to the list

USAGE:
   main add [command options] [arguments...]

OPTIONS:
   --json value, -j value  (default: "add.json")
   --exec, -e              

# completeコマンドを実行。addコマンドに指定したFlagsを指定
$ go run src/script/cli_43/main.go complete -c test.csv
completed task:  
csv           :  test.csv
exec          :  false

おわり

urfave/cli いろいろてんこもりで便利ですね。
ここまでがっつり機能いらないよって場合は、標準のflag パッケージの方を使ってサクっと作るのも良さそうです
\(^o^)/

【go】direnvで環境(プロジェクトやディレクトリ)ごとに環境変数を分けるメモ - 例:GOPATH

はじめ

golangに限らないですが、
環境ごとに環境変数を切り替えたいときはまぁまぁありますよね。
そんなときのdirenvメモです。

mac x golangな環境を前提に書きますが
direnv自体はlinuxでも他の言語や環境でも同じです。

アジェンダ

  1. direnvとは
  2. direnvのinstall&設定
  3. direnv使ってみる
  4. direnv: error .envrc is blocked. Run direnv allow to approve its content.なとき

1. direnvとは

まずはdirenvのリポジトリ
https://github.com/direnv/direnv

簡単にいうとプロジェクトやディレクトリごとに環境変数を勝手に切り替えてくれる君です。
公式から引用するとこんな感じ。

direnv is an environment switcher for the shell. It knows how to hook into bash, zsh, tcsh and fish shell to load or unload environment variables depending on the current directory. This allows project-specific environment variables without cluttering the “~/.profile” file.

使うとこんな事が可能になります。

* ディレクトリごとにGPATHを設定できる
* ディレクトリへ移動すると自動的に設定される(出ると勝手に解除)

2. direnvのinstall&設定

使う前のinstall&設定。簡単です

# install
$ brew update
$ brew install direnv

# shellに設定
$ echo 'eval "$(direnv hook bash)"' >> ~/.bashrc

3. direnv使ってみる

設定してみる

ここでは環境(ディレクトリ)ごとにGOPATHを切り替える という例で書きますが、他の環境変数でも同じです。

# 移動
$ cd path/to/設定したいディレクトリ

# .envrcを作成
$ direnv edit .
---- vi追記 ----
export GOPATH=$(pwd) 
--------------- 

使い方

# direnvを設定したdir以外にいる前提
$ pwd
path/to/direnv設定してないdir

# 移動
$ cd path/to/設定したdir
direnv: loading .envrc
direnv: export ~GOPATH

# 出てみる
$ cd ..
direnv: unloading

補足

direnv edit .でエラー

direnv edit .するとdirenv: $EDITOR not found なエラーができるときは$EDITORを設定してやります

# 怒られる
$ direnv edit .
direnv: $EDITOR not found.

# bashrcに設定&読み込み
$ echo 'export EDITOR=vim' >> ~/.bashrc
$ source ~/.bashrc

# vimが開けばok
$ direnv edit .

4. direnv: error .envrc is blocked. Run direnv allow to approve its content.なとき

.envrcを直接編集すると以下のようなエラーが出るときがあります。
編集後は安全のためにdirenv allowで明示的に読み込むまでは無効になっているよ、という事らしいです。

# 何かしたらこれがでる
direnv: error .envrc is blocked. Run `direnv allow` to approve its content.

# 素直に打つ
$ direnv allow

参考

終わり

いまさら direnv の解説という記事を参考にさせてもらって
さらに今さら感がありますが自分用メモ>\(^o^)/