write ahead log

ロールフォワード用

catの-(ハイフン)オプションを使う

こんなファイルを用意する.

$ cat > test.txt <<EOS
test
test
test
test
EOS
$ cat > test2.txt <<EOS
test2
test2
test2
test2
EOS

これを普通にパイプでつなぐとパイプで入ってきた入力は無視される.

$ cat test.txt | cat test2.txt
test2
test2
test2
test2

-(ハイフン)を使うとファイルの入力のように扱うことができる.

標準入力を第一引数に指定する感じ.

$ cat test2.txt | cat - test.txt
test2
test2
test2
test2
test
test
test
test

標準入力を第二引数に指定する感じ.

$ cat test2.txt | cat test.txt -
test
test
test
test
test2
test2
test2
test2

ワンライナーとかで便利かも.

opensslを使って鍵の作成・ファイルの暗号化・復号化

opensslを使うと秘密鍵,公開鍵を作ってファイルを暗号化したり,復号化したりできる.

とても面白いのだけど,オプションが多くてややこしいのでメモしておく.

秘密鍵を作る

seckey.pemという名の秘密鍵を作る場合.

$ openssl genrsa -out seckey.pem -aes256 2048
  # 秘密鍵のパスワードとパスワードの確認入力が求められます

公開鍵を作る

seckey.pemという名の秘密鍵を使って,pubkey.pemという名の公開鍵を作る場合.

$ openssl rsa -in seckey.pem -pubout -out pubkey.pem

ファイルを暗号化する

secret.txtを暗号化して,secret.txt.secretを作ってみます.

とりあえずsecret.txtを作ります.

$ cat > secret.txt <<EOS
 this is a
 secret
 text.
EOS

続いて暗号化

$ openssl rsautl -pubin -inkey pubkey.pem -in secret.txt -encrypt -out secret.txt.secret

よくわからない内容のファイルができています.

$ cat secret.txt.secret
K>Fp] 4{^ F'ݿqgs&@ߟ%[jÜ
I50@1{._j?@w"BkAX+'
6>IgޓM=Fv^1v    p+zUޟ/Nؽ{$QR؃І+C
                               0>jB~*

ファイルを復号化する

seckey.pemを使ってsecret.txt.secretを復号化します.

cat secret.txt.secret | openssl

実行結果

$ cat secret.txt.secret | openssl rsautl -decrypt -inkey seckey.pem
Enter pass phrase for seckey.pem:  # 秘密鍵のパスワードを入力
this is a
secret
text.

割と簡単にできるんですね.

golangでファイル・ディレクトリを操作する

ちょっとしたツールを作るのにも, DBを使うのが面倒な時にもファイル操作は便利.

サンプルコードの実行にはosパッケージをインポートしてください. (Printしてるものには当然fmtも)

import (
    "os"
    "path/filepath" //ファイルパス操作の時は
)
ディレクトリを作る
// パーミッション0777でfooを作成
if err := os.Mkdir("foo", 0777); err != nil {
    panic(err)
}
サブディレクトリも作る

サブディレクトリもまとめて作りたい時

// パーミッション0777でhoge/fugaを作成(サブディレクトリもまとめて作る)
if err := os.MkdirAll("hoge/fuga", 0777); err != nil {
    panic(err)
}
ファイル・ディレクトリの名前変更

fooをbarに変更

if err := os.Rename("foo", "bar"); err != nil {
    panic(err)
}
ファイル・ディレクトリの移動

fooの下のtest.txtをbarへ移動.こちらもRenameを使う(どうなんだろう,これ)

if err := os.Rename("foo/test.txt", "bar/test.txt"); err != nil {
    panic(err)
}
ファイル・ディレクトリの権限変更

コマンドのchmodと変わらないですね.

if err := os.Chmod("foo", 0666); err != nil {
    panic(err)
}
ファイル・ディレクトリの削除

下記では中身のあるディレクトリは消せません(rmdirと同じですね)

if err := os.Remove("foo"); err != nil {
    panic(err)
}
ファイル・ディレクトリの削除(中のファイルごと)

これで中身があろうと消せます.

if err := os.RemoveAll("foo"); err != nil {
    panic(err)
}
ファイル・ディレクトリの存在確認

statでエラーが無いかで判断するのが良いようです.

_, err := os.Stat("foo.txt")
if err == nil {
    fmt.Println("あった")
} else {
    fmt.Println("なかった")
}

もうちょっと複雑な操作

ここから先はfilepathもインポートしてください. (Printしてるものには当然fmtも)

import (
    "os"
    "path/filepath"
)
指定ディレクトリ下のファイル・ディレクトリ一覧の取得

filepath.Walkを使います.
これはちょっとややこしいので全部書きます.
walk_funcは匿名関数の方がわかりやすい場合もあるかもしれません.

package main

import (
    "fmt"
    "os"
    "path/filepath"
)

func walk_func(path string, info os.FileInfo, err error) error {
    // ディレクトリか判断
    if info.IsDir() {
        // hoge/foo以下は無視する
        if path == "hoge/foo" {
            // 無視するときはfilepath.SkipDirを戻す
            return filepath.SkipDir
        }
    }
    // パスを印字
    fmt.Println(path)
    // 普通はnilを戻す
    return nil
}

func main() {
    root_dir := "."
    err := filepath.Walk(root_dir, walk_func)

    if err != nil {
        panic(err)
    }
}

実行結果

ディレクトリが荒れててちょっとわかりづらいですが.

twinbird@:~/go/src/file_operate$ ./file_operate 
.
bar
bar/test.txt
blog.txt
file_operate
foo
hoge
sample.go
twinbird@:~/go/src/file_operate$ tree .
.
├── bar
│   └── test.txt
├── blog.txt
├── file_operate
├── foo
├── hoge
│   └── foo
│       └── test2.txt
└── sample.go

ちょっと解説入れると

  • filepath.Walkはディレクトリやファイルが見つかるたびにwalk_funcをコールバックで呼び出すことになります.
  • コールバックのinfoにはファイル情報が入っているので参照できます.
  • コールバックの戻り値にfilepath.SkipDirを使えば,走査中の配下のディレクトリは無視するようです.基本はnilを戻します.

ファイルパスの操作

ファイル操作するときにはファイルパスの操作もセットみたいなもんでしょう.

絶対パスを取得する

filepath.Absを使います.

abs_path, err := filepath.Abs(".")
if err != nil {
    panic(err)
}
fmt.Println(abs_path)

実行結果

/home/twinbird/go/src/file_operate
ファイル名を取得する
base_path := filepath.Base(`C:\Users\IMAI\Desktop\test\a.txt`)
fmt.Println(base_path)

実行結果

a.txt
ファイルパスを連結する
base_path := `C:\Users\IMAI\Desktop\test`
file_path := "a.txt"
joined_path := filepath.Join(base_path, file_path)
fmt.Println(joined_path)

実行結果

C:\Users\IMAI\Desktop\test\a.txt
拡張子を取得する
file_path := "a.txt"
ext := filepath.Ext(file_path)
fmt.Println(ext)

実行結果

.txt

goでコマンドラインツールを作る時にflagパッケージを使う

go言語(golang)はマルチプラットフォームでバイナリを吐けるのでコマンドラインツールを作るにはちょうどいい言語だと思います.

標準パッケージも充実していて,オプションやメッセージを出力するパッケージが標準で準備されています.

当初ググると以下が人気だということだったんですが,僕は割と保守的なので標準パッケージ使います.

GitHub - tcnksm/gcli: The easy way to build Golang command-line application.

osパッケージを使う

一番基礎的な扱い方はOSパッケージを使う方法でしょう. Cを使ったことがある人なら馴染みある感じです.

サンプルソース

package main

import (
    "fmt"
    "os"
)

func main() {
    fmt.Println(os.Args)    // => [./flag_sample arg1 arg2]
    fmt.Println(os.Args[0]) // => ./flag_sample
    fmt.Println(os.Args[1]) // => arg1
    fmt.Println(os.Args[2]) // => arg2
}

os.Argsにはプログラムのパス(index:0)と引数(index:1以降)が入ります.

実行結果

twinbird@:~/go/src/flag_sample$ ./flag_sample arg1 arg2
[./flag_sample arg1 arg2]
./flag_sample
arg1
arg2

flagパッケージを使う

引数を扱う時にはCで言うところのgetoptsみたいなものが欲しくなると思います.
(といいつつ僕もほとんど使ったことがないですが)

そこでflagパッケージがあります.
色々面倒事を引き受けてくれる便利なものです.これが標準なんだからいいもんです.

flagパッケージで値を受け取るには2つの方法があります.

  1. 変数を宣言してflag.xxxVarを呼び出す
  2. flag.xxxを呼び出して戻り値を受け取る

(xxxはそれぞれ型名)

flag.xxxVarを使う場合
package main

import (
    "fmt"
    "flag"
)

func main() {
    /* 
    * 1. `force`という名前のフラグで
     * 2. デフォルト値は`false`.
    * 3. Usageとして`無理やり何かする`を表示
    */
    b := flag.Bool("force", false, "無理やり何かする") 

    /* 使う前にParseしないとならない. */
    flag.Parse()

    /* ポインタが返されるので間接参照のためにアスタリスクつける */
    fmt.Println(*b)
}

実行結果

twinbird@:~/go/src/flag_sample$ ./flag_sample 
false # => デフォルトのfalseが表示される

twinbird@:~/go/src/flag_sample$ ./flag_sample force=true
false # => -か--をオプションとみなすのでデフォルトのfalseが表示される(force=trueは単なる第一引数になる)

twinbird@:~/go/src/flag_sample$ ./flag_sample -force=true
true # => -forceオプションをtrueにしたのでtrue

twinbird@:~/go/src/flag_sample$ ./flag_sample --force=true
true # => --もオプションとみなすのでtrueになる

twinbird@:~/go/src/flag_sample$ ./flag_sample ---force=true
bad flag syntax: ---force=true
Usage of ./flag_sample:
  -force
          無理やり何かする

# => ---はオプション扱いしない.
#    フラグ指定がおかしいのでUsageが表示される.
#    Usageは指定してあった`無理やり何かする`が表示される

twinbird@:~/go/src/flag_sample$ ./flag_sample -force true
true # => forceとtrueの間に=がなくても認識される

twinbird@twinbird-pc:~/Dropbox/go/src/flag_sample$ ./flag_sample -h
Usage of ./flag_sample:
  -force
          無理やり何かする

# => -hオプションをつけるとすべてのオプションのUsageを表示してくれる.
flag.xxxを使う場合

それほど変わらない.
変数を先に定義してアドレスを渡すか、ポインタを戻してもらうかの違い.
個人的にはこちらのほうが使い勝手がよい.

package main

import (
    "fmt"
    "flag"
)

func main() {
    var b bool
    flag.BoolVar(&b, "force", false, "無理やり何かする")
    flag.Parse()
    fmt.Println(b)
}
ヘルプ(Usage)を作る

-hでのUsageの説明文もカスタマイズすることができる.

package main

import (
    "fmt"
    "flag"
    "os"
)

func main() {
    var b bool
    flag.Usage = func() {
        fmt.Fprintf(os.Stderr, "使い方:これこれこんな感じ.\n")
        flag.PrintDefaults() // => オプションの説明文を表示
    }
    flag.BoolVar(&b, "force", false, "無理やり何かする")
    flag.Parse()
    fmt.Println(b)
}

実行結果

twinbird@:~/go/src/flag_sample$ ./flag_sample -h
使い方:これこれこんな感じ.
  -force
          無理やり何かする
引数の数を得る

NArg()で得ることができます.flag.Parse()の実行後出ないと出ません.

package main                                                                                                                     
 
import (
    "fmt"
    "flag"
)

func main() {
    var b bool
    flag.BoolVar(&b, "force", false, "無理やりやる")
    flag.Parse()
    fmt.Println(flag.NArg())
}

実行結果

twinbird@:~/go/src/flag_sample$ ./flag_sample -force true arg2
2

-forceがtrueということで3つではなく2つとちゃんと認識してますね.

1つのオプションに2つの指定方法を与える

xxxVarを利用するなら単に同じ変数を与えてやればよい.

package main

import (
    "fmt"
    "flag"
)

func main() {
    var b bool
    flag.BoolVar(&b, "force", false, "無理やりやる")
    flag.BoolVar(&b, "f", false, "無理やりやる")
    flag.Parse()
    fmt.Println(b)
}

実行結果

twinbird@:~/go/src/flag_sample$ ./flag_sample 
false
twinbird@:~/go/src/flag_sample$ ./flag_sample -f true
true
twinbird@:~/go/src/flag_sample$ ./flag_sample -force true
true
指定されたオプションを順に処理する

visitを利用すれば良いようです.
visitAllにすれば指定されていないオプションも対象にするようです.

package main

import (
    "fmt"
    "flag"
)

func main() {
    var b bool
    var s string
    var i int
    flag.BoolVar(&b, "force", false, "無理やりやる")
    flag.IntVar(&i, "times", 0, "繰り返す")
    flag.StringVar(&s, "category", "", "カテゴリー指定")
    flag.Parse()
    fmt.Println(b)
    fmt.Println(s)
    fmt.Println(i)

    visit_str := ""
    flag.Visit(func(f *flag.Flag) {
        visit_str += fmt.Sprintf("%s: %v\n", f.Name, f.Value)
    })
    fmt.Println(visit_str)


    visitAll_str := ""
    flag.VisitAll(func(f *flag.Flag) {
        visitAll_str += fmt.Sprintf("%s: %v\n", f.Name, f.Value)
    })
    fmt.Println(visitAll_str)
}

実行結果

twinbird@:~/go/src/flag_sample$ ./flag_sample -times=10 -force=true
true

10
force: true # <- ここからVisit(指定されたものだけ対象にするのでcategoryが出ない)
times: 10

category:   # <- ここからVisitAll(指定されていなくても対象になるのでデフォルト値が出る)
force: true
times: 10
FlagSet

カスタマイズしたフラグを作ったりするのに使えそうです.
公式を元に調べて別記事にしないとダメですね.
今回使ってません.

まとめ

これだけ機能があれば標準でも十分な気がします.

gitでUntracked filesを削除する

作業しているとgitで追跡したくないファイルがたくさん生成されて,邪魔になる事がよくある.

git cleanという一括で削除するコマンドがあった.

使い方

削除する対象を確認する
$git clean -n
実際に削除する
$git clean -f

上記だとディレクトリは削除対象にならない.

削除する対象を確認する(ディレクトリも対象)
$git clean -nd
実際に削除する(ディレクトリも対象)
$git clean -fd

golangの日付フォーマット指定が面白い

百聞は一見に如かずということでとりあえずサンプルを.

package main

import (
    "fmt"
    "time"
)

const (
    DATE_TIME_FORMAT = "2006/01/02/15:04:05"
)

func main() {
    now := time.Now().Format(DATE_TIME_FORMAT)
    fmt.Println(now)
}

実行結果

$ ./sample.exe
2016/04/26/17:48:42

日付フォーマット指定が
2006年1月2日の15時4分5秒
になっている.

これはgolangのtimeパッケージの規約のようだ.

timeパッケージのドキュメントを読むといたるところに出現する.

constで用意までされている.

   const (
    ANSIC       = "Mon Jan _2 15:04:05 2006"
    UnixDate    = "Mon Jan _2 15:04:05 MST 2006"
    RubyDate    = "Mon Jan 02 15:04:05 -0700 2006"
    RFC822      = "02 Jan 06 15:04 MST"
    RFC822Z     = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
    RFC850      = "Monday, 02-Jan-06 15:04:05 MST"
    RFC1123     = "Mon, 02 Jan 2006 15:04:05 MST"
    RFC1123Z    = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
    RFC3339     = "2006-01-02T15:04:05Z07:00"
    RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
    Kitchen     = "3:04PM"
    // Handy time stamps.
    Stamp      = "Jan _2 15:04:05"
    StampMilli = "Jan _2 15:04:05.000"
    StampMicro = "Jan _2 15:04:05.000000"
    StampNano  = "Jan _2 15:04:05.000000000"

軽く整理すると以下のようになる.

指定 意味
2006
01
02
15
04
05

これはなかなか斬新である. 慣れるまでは違和感がすごいけど, 慣れると結構見やすいのかもしれない.

どちらにしても先進的な言語である.

golangでJSONをパースする

いろいろな所に書かれているけど,ハマってしまったので書き残しておく.

golangでJSONをパースするにはencoding/jsonを利用すれば良い.

静的言語だけあってちょっと面倒なところがあって,JSONの構造に合わせて前もってstructを定義しておく必要がある.

基本

一番単純な例だと以下のようなJSONを考える.

{
    "name":"twinbird",
    "age":27,
    "sex":"man"
}

これをパースして表示しようとすると以下のようなコードになる.

package main

import (
    "encoding/json"
    "fmt"
)

const (
    json1 = `
      {
          "name":"twinbird",
          "age":27,
          "sex":"man"
      }`
)

type Person struct {
    Name string `json:"name"`
    Age int `json:"age"`
    Sex string `json:"sex"`
}

func main() {
    bytes := []byte(json1)
    var p Person
    err := json.Unmarshal(bytes, &p)
    if err != nil {
        fmt.Println("error:", err)
        return
    }

    fmt.Printf("Name: %v, Age: %v, Sex: %v\n",
        p.Name, p.Age, p.Sex)
}

結果は以下になる.

Name: twinbird, Age: 27, Sex: man

ここまでは割と単純だ.

大事なのはstructにタグ(`で囲んだやつ)を付けてJSONのキーに合わせてやること.

タグは単なるメタデータらしいのでライブラリに知らせるためだけに使われることになる.
(大文字,小文字に注意.また,キーはダブルクオーテーションで囲む必要がある.)

ところでタグの付け方がおかしいかを確認するコマンドがある

$ go vet

コンパイル前におかしい箇所を色々指摘してくれる.

配列

次にコレを応用して配列をパースする.
実は次のネストした関係よりこちらの方にハマってしまった. 大したことではないのだが,人間一度視野が狭くなると中々抜け出せない.

package main

import (
    "encoding/json"
    "fmt"
)

const (
    json1 = `
  [
      {
          "name":"twinbird",
          "age":27,
          "sex":"man"
      },
      {
          "name":"taro",
          "age":29,
          "sex":"man"
      }
  ]`
)

type Person struct {
    Name string `json:"name"`
    Age int `json:"age"`
    Sex string `json:"sex"`
}

func main() {
    bytes := []byte(json1)
    var people []Person
    err := json.Unmarshal(bytes, &people)
    if err != nil {
        fmt.Println("error:", err)
        return
    }

    for _, person := range people {
        fmt.Printf("Name: %v, Age: %v, Sex: %v\n",
            person.Name, person.Age, person.Sex)
    }
}

ほとんど変わっていない.
だが,とても大事なことはjson.Unmarshalに渡す変数がスライスになった事だ.

やってしまえば当たり前なのだが,随分ハマってしまった.

実行結果は以下

Name: twinbird, Age: 27, Sex: man
Name: taro, Age: 29, Sex: man

ネスト

ネストしたデータは構造体のネストで表す.

package main

import (
    "encoding/json"
    "fmt"
)

const (
    json1 = `
  {
      "company_name":"FooSystem",
      "employees":[
          {
              "name":"twinbird",
              "age":27,
              "sex":"man"
          },
          {
              "name":"taro",
              "age":29,
              "sex":"man"
          }
      ]
  }`
)

type Company struct {
    Name string `json:"company_name"`
    Employees []Person `json:"employees"`
}

type Person struct {
    Name string `json:"name"`
    Age int `json:"age"`
    Sex string `json:"sex"`
}

func main() {
    bytes := []byte(json1)
    var c Company
    err := json.Unmarshal(bytes, &c)
    if err != nil {
        fmt.Println("error:", err)
        return
    }

    fmt.Println(c.Name)
    for _, person := range c.Employees {
        fmt.Printf("Name: %v, Age: %v, Sex: %v\n",
            person.Name, person.Age, person.Sex)
    }
}

これも割と素直.

ちょっと楽する

ところで構造体にタグを書いていましたが実は書かなくて良い状況があります.

JSONのキーが構造体名と完全一致,または大文字小文字の差だけであれば勝手にマッピングしてくれます.

最初の例(タグ無し版)

package main

import (
    "encoding/json"
    "fmt"
)

const (
    json1 = `
      {
          "name":"twinbird",
          "age":27,
          "sex":"man"
      }`
)

type Person struct {
    Name string
    Age int
    Sex string
}

func main() {
    bytes := []byte(json1)
    var p Person
    err := json.Unmarshal(bytes, &p)
    if err != nil {
        fmt.Println("error:", err)
        return
    }

    fmt.Printf("Name: %v, Age: %v, Sex: %v\n",
        p.Name, p.Age, p.Sex)
}

結果は以下になる.

Name: twinbird, Age: 27, Sex: man

まとめ

  • JSONのパースには構造体にタグ付けをしてUnmarshal
  • 配列は構造体を素直にスライスにする
  • ネストも構造体を素直にネストする
  • 実はJSONのキーと構造体名が同じならタグ付け不要