Help us understand the problem. What is going on with this article?

go で file を touch する

この記事について

go 言語でアプリケーションを書いていて、ファイルを touch したくなった。
でも、 os には touch がない。

どうしたものかとツイートしたら

https://twitter.com/cielavenir/status/1172472063837102084

という応答を頂いたので、書いてみた。

ソースコード

package gotouch

import (
    "runtime"
    "syscall"
    "time"
)

// Touch は、 macOS などに含まれる touch コマンドを模倣する
func Touch(path string) error {
    now := time.Now().UnixNano()
    const nano = 1000 * 1000 * 1000
    t := syscall.Timespec{Sec: now / nano, Nsec: now % nano}
    ut := []syscall.Timespec{t, t}
    err := syscall.UtimesNano(path, ut)
    if err == nil {
        return nil
    }
    isNoEnt := func(e error) bool {
        if e == syscall.ENOENT {
            return true
        }
        switch runtime.GOOS {
        case "windows":
            // Windows では syscall.ERROR_PATH_NOT_FOUND を返すことがある。
            // しかし、非 Windows 環境では syscall.ERROR_PATH_NOT_FOUND は定義されない。
            // ifdef も使えない。別ファイルにするのはめんどくさい。
            // 仕方ないので自分で定義する。
            const ERROR_PATH_NOT_FOUND = 3 // 「don't use ALL_CAPS in Go names」と言われるが、 syscall に合わせる。
            if errno, ok := e.(syscall.Errno); ok && errno == ERROR_PATH_NOT_FOUND {
                return true
            }
            return false
        case "darwin":
            return false
        case "linux":
            // たぶん OK だけど、テストしてない。
            return false
        default:
            panic("you should write something here")
        }
    }
    if !isNoEnt(err) {
        return err
    }
    fd, err := syscall.Open(path, syscall.O_CREAT|syscall.O_RDWR, 0644)
    if err != nil {
        return err
    }
    defer syscall.Close(fd)
    return nil
}

かるく説明

日本語で touch の説明をすると

  • ファイルがあれば、そのファイルのタイムスタンプを更新する
  • なければ、ファイルを作る

という感じになると思う。
しかし、上記の日本語のとおりに実装すると、《ファイルがある》と思ってから《タイムスタンプを更新する》という操作の間に誰かがファイルを削除する可能性があり、よろしくない。

そこで実際は

  • タイムスタンプの更新を試みる
  • 「ファイルがない」という理由で更新に失敗した場合、 O_TRUNC指定せずにファイルを作る

という実装になる。

O_TRUNC なしで作ることによって《「ファイルがない」という理由で更新に失敗》のあと、《ファイルを作る》という操作の前に誰かがファイルを作った場合でも不幸になることがない。

なお。
上記のソースコードは手元の macOS と Windows で動作確認した。
Linux では動かしてないけど大丈夫じゃないかと思っている。

macOS, Windows, Linux 以外だと死ぬ。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away