tkuchikiの日記

新ブログ https://blog.tkuchiki.net

Golang で filepath.Abs に ~(ホームディレクトリ) を指定した時にカレントワーキングディレクトリが先頭についてしまう

検証した環境は以下のとおりです。

$ go version
go version go1.4.2 linux/amd64
$ whoami
tkuchiki
$ touch ~/testfile
$ cd /tmp

した状態で、

// main.go
package main

import (
        "fmt"
        "path/filepath"
)

func main() {
        abspath, _ := filepath.Abs("~/testfile")
        fmt.Println(abspath)
}

を実行してみたところ、

$ go run main.go
/tmp/~/testfile

と出力されました。
カレントワーキングディレクトリが ~/ の前につくようですので、
/tmp/~ を実行ユーザのホームディレクトリに置換するようにしました。

追記1:

id:hnakamur3 さんからご指摘いただきました。
ありがとうございます。

http://golang.org/src/path/filepath/path.go?s=6548:6585#L223http://golang.org/src/path/filepath/path_unix.go?s=313:341#L2 を見ると、
/で始まってなければIsAbs()がfalseになってAbs()でカレントディレクトリをくっつけるようになっています。
なので、Absを呼ぶ前に~をusr.HomeDirに置換するのが順当かなと思います。

確かにそのとおりですね。
あと、user.HomeDir絶対パスを返すので、
filepath.Abs() を実行しなくても、~user.HomeDir で置換するだけで良さそうです。

// main2.go
package main

import (
        "fmt"
        "os/user"
        "strings"
)

func main() {
        usr, _ := user.Current()
        // usr.HomeDir = ホームディレクトリ
        f := strings.Replace("~/testfile",  "~", usr.HomeDir, 1)
        fmt.Println(f)
}

以下が実行結果です。

$ go run main2.go
/home/tkuchiki/testfile

追記2:

mattn さんから、以下のように教えていただきました。
ありがとうございます。

~ はシェルで提供している機能なので、
Golang に実装されていなくてもおかしくないですね。
提案が却下されてしまったのは残念でした..

あまり調査していないので、
もっと簡単にやる方法があるかもしれません(あってほしい)。

修正前のコードを残しておきます。

// main2.go
package main

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

func main() {
        wd, _ := os.Getwd() // カレントワーキングディレクトリ
        usr, _ := user.Current()
        abspath, _ := filepath.Abs("~/testfile")
        // usr.HomeDir = ホームディレクトリ
        f := strings.Replace(abspath, fmt.Sprintf("%s/~", wd), usr.HomeDir, 1)

        fmt.Println(f)
}