プログラミング言語はそれぞれに特徴があって、得意、不得意な部分が、それぞれあると思う。

そんな中、go 言語には以前から興味があったが、仕事上、なかなか使う機会が回ってこなかった。

tour of go を一通り読んでいたので、go の仕事も請けられるだろう、と楽観視していたところ、今回、go で作ってもよいということになったので、本腰を入れて勉強することにした。(緩いなあ・・・)

フォルダ内のファイルの md5 を記録する

私のプログラミング言語の勉強方法としては、簡単なテーマを決めて、それを実装してみるという方法。

ディスク上に重複したファイルが増えてきて、重複したファイルを削除したいな、と思っていたので、手始めとして、指定したディレクトリのファイルサイズと md5 sum を sqlite3 db に書き込むといったことをやってみようと思った。

そのコード。

package main

import (
    "fmt"
    "os"
    "time"
    "path/filepath"
    "crypto/md5"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/sqlite"
    "log"
    "io"
    "encoding/hex"
)

type FileInfo struct {
    gorm.Model
    Name string
    Size int64
    ModTime time.Time
    MD5 string
}

func WalkAndRegister(db *gorm.DB) filepath.WalkFunc {
    return func (path string, info os.FileInfo, err error) error {
        fi, err := os.Stat(path)
        if err != nil {
            return err
        }

        if fi.IsDir() {
            return nil
        }

        f, err := os.Open(path)
        if err != nil {
            return err
        }
        defer f.Close()

        h := md5.New()
        if _, err := io.Copy(h, f); err != nil {
            log.Fatal(err)
        }

        db.Create(&FileInfo{Name: path, Size: fi.Size(), ModTime: fi.ModTime(), MD5: hex.EncodeToString(h.Sum(nil))})
        fmt.Printf("%s\n", path)
        return nil
    }
}

func main() {
    dirs := os.Args[1:]
    if len(dirs) == 0 {
        wd, err := os.Getwd()
        if err != nil {
            return
        }
        dirs = append(dirs, wd)
    }
    dbfile := "/tmp/dupcheck.db"
    db, err := gorm.Open("sqlite3", dbfile)
    if err != nil {
        panic("failed to connect database")
    }
    defer db.Close()

    db.AutoMigrate(&FileInfo{})

    for i := range dirs {
        filepath.Walk(dirs[i], WalkAndRegister(db))
    }
}

ビルドと実行


sudo yum install go (mac なら brew install など)
mkdir dupcheck
cd dupcheck
vim dupcheck.go

go build dupcheck.go とすると、なんやかんやエラーが出るので、以下を追加で実行。

go get -u github.com/jinzhu/gorm
go get github.com/mattn/go-sqlite3

再度、go build dupcheck.go を実行すると、dupcheck という実行ファイルができる。

./dupcheck 適当なディレクトリ

を実行すると /tmp/dupcheck.db ができる。中身を見たければ、sqlite3 /tmp/dupcheck.db を実行して、select * from file_infos; で見る。

そうなんや、と感心した点

  • github からライブラリを import できる点 — 昔はたくさんライブラリを取ってきて configure しないといけなかった(いつの時代や)のに、相当便利になったなあ
  • ビルドシステムが内蔵されているのか makefile のようなめんどうなものが不要
  • defer で関数から戻った時に処理を実行させられる点 — 組み込みにこんな安全な機能があったら・・・
  • (私のような C 言語で止まっているような者には理解しづらい)関数の外側にある変数を持った関数を返せる点(closure) — filepath.Walk でどうやって db を渡すん?と思った
  • 例外でなく、error を返す方針 — 私のような C 言語で止まっている者には例外よりわかりやすいかも

おわりに

やはり、「コンピュータは習うより慣れろ」(古い・・・) だと思うので、塾に通う前によかったらご参考に。

それと、go 言語のチュートリアルはわかりやすくインターラクティブに作られていると思うので、興味があれば部分的にでも試してみることをおすすめしたい。