1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Goでファイルを書き込むと書き込み先ファイルが消えているのに読み込める

Last updated at Posted at 2024-10-05

症状

準備

以下のようなログラムを作成した.

package main

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

func main() {
	path := "~/.config/tilde-expan/hoge.txt"
	write(path)
	read(path)
}

func write(path string) {
	dir := filepath.Dir(path) // ディレクトリが存在しない場合は作成する
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		err = os.MkdirAll(dir, 0755)
	}

	file, _ := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
	defer file.Close()

	fmt.Fprintln(file, "test")
}

func read(path string) {
	file, _ := os.Open(path)
	defer file.Close()

	byteData, _ := io.ReadAll(file)
	content := string(byteData)
	fmt.Println("content = ", content)
}

実行の流れは以下の通り.

  1. main開始.
  2. write呼び出し.
    1. ディレクトリが存在しなければ作成する.
    2. 指定したパスに"test"と書き込む.
  3. read呼び出し.
    1. 指定したパスのファイル内容を読み込む.
    2. 読み込んだ内容を出力する.

実行結果

以下のように,特にエラーも出ず,書き込んだファイルの中身を表示することができる.

$ go run main.go
content =  test

しかし,保存先であるフォルダに移動しようとしてもフォルダやファイルは存在しない.

$ cd ~/.config/tilde-expan
cd: no such file or directory: /Users/USERNAME/.config/tilde-expan
$ cat  cd ~/.config/tilde-expan/hoge.txt
cat: cd: No such file or directory
cat: /Users/USERNAME/.config/tilde-expan/hoge.txt: No such file or directory

デバッグ

プログラム

以下のように,各所でデバッグプリントを入れてみる.

package main

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

func main() {
	path := "~/.config/tilde-expan/hoge.txt"
	write(path)
	read(path)
}

func write(path string) {
	dir := filepath.Dir(path) // ディレクトリが存在しない場合は作成する
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		fmt.Println("isNotExist", err)
		err = os.MkdirAll(dir, 0755)
		fmt.Println("MkdirAll", err)
	}

	file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
	fmt.Println("OpenFile", err)
	defer file.Close()

	_, err = fmt.Fprintln(file, "test")
	fmt.Println("Fprintln", err)
}

func read(path string) {
	file, err := os.Open(path)
	fmt.Println("Open", err)
	defer file.Close()

	byteData, err := io.ReadAll(file)
	fmt.Println("ReadAll", err)

	content := string(byteData)
	fmt.Println("content = ", content)
}

結果

各エラー出力は全て<nil>
ついでにcontetnが2行に増えているので,正しく追記できていることもわかる.

$ go run main.go
isNotExist stat ~/.config/tilde-expan: no such file or directory
MkdirAll <nil>
OpenFile <nil>
Fprintln <nil>
Open <nil>
ReadAll <nil>
content =  test
test

原因

Goのパス指定はチルダに対応していない.
これだけだった.

Goのプログラムと同じ場所でlsコマンドを実行すると,同じ場所に~ディレクトリが生成されている.

$ ls
README.md    main.go      ~

~がエクスパンドされないようにパスを"で囲んでcatコマンドを実行すると,先ほど書き込んだファイルの中身が表示される.

$ cat "~/.config/tilde-expan/hoge.txt"
test
tes

つまり,~がホームディレクトリとして認識されず,そのまま~ディレクトリだと認識されていた.

解決策

~エクスパンド処理を追加する.
以下のような関数を作る.

func expandTilde(path string) (string, error) {
	if path == "~" || strings.HasPrefix(path, "~/") {
		user, err := user.Current()
		if err != nil {
			return "", err
		}
		if path == "~" {
			return user.HomeDir, nil
		}
		return filepath.Join(user.HomeDir, path[2:]), nil // ~/ を切り取る
	}
	return path, nil
}

以下のようにしてmain関数に組み込めむ.

func main() {
	path := "~/.config/tilde-expan/hoge.txt"
	actualPath, _ := expandTilde(path)
	fmt.Println("actualPath = ", actualPath)
	write(actualPath)
	read(actualPath)
}

パスが正しく変換できていることがわかる.
また,目的通りのパスに保存されていることもわかる.

$ go run ./main.go
actualPath =  /Users/USERNAME/.config/tilde-expan/hoge.txt
content =  test
$ cat ~/.config/tilde-expan/hoge.txt
test

サンプルプログラム

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?