earthly/earthlyに修正のPRを出す過程で知ったのでメモ github.com
例えば /Users/hoge/ccc
と /Users/hoge/CCC
というふたつのパスがあるとする。
クロスプラットフォームなCLIアプリケーションを作っていたりすると、上記のような2つのパスが「同じ場所を指しているか」を比較するにあたってMacOSやLinux, Windowsのファイルシステムでcase-sensitivityを考慮して実装したくなるかもしれない。
これを何も考えずに解決しようとするとこういうのが思い浮かぶ。
pathA := "/Users/hoge/ccc" pathB := "/Users/hoge/CCC" if runtime.GOOS == "darwin" { pathA = strings.ToLower(pathA) pathB = strings.ToLower(pathB) } if pathA == pathB { fmt.Println("path matched") }
APFSはデフォルトではcase-insenstiveなので場当たり的によさそうではあるが、デフォルトでそうというだけであってフォーマット時にcase-sensitiveを選択することもできる。つまり、厳密にはOSを見るのではなくファイルシステムを考慮してcase-sensitivityを判別しないといけないことになる。
じゃあファイルシステムをチェックする実装を... するのはイヤなので、こういうときには os.SameFile
という関数を使うのがよい。
os.SameFile
によるパス比較
雑に書き換えてみるとこういう感じになる。
pathA := "/Users/hoge/ccc" pathB := "/Users/hoge/CCC" pathAInfo, _ := os.Stat(pathA) pathBInfo, _ := os.Stat(pathB) if os.SameFile(pathAInfo, pathBInfo) { fmt.Println("path matched") }
os.SameFile
は内部的にはgodocに書かれているように次の挙動をするとのこと。
For example, on Unix this means that the device and inode fields of the two underlying structures are identical; on other systems the decision may be based on the path names.
Unixな環境ではパス名を文字列で比較することはせずinodeによって比較をするらしい。それ以外の環境ではパス名による比較が行われる。嬉しい抽象化だね。