コンテンツにスキップ

Go/Standard library/os

出典: フリー教科書『ウィキブックス(Wikibooks)』

osパッケージの概要

[編集]

osパッケージは、Go言語においてオペレーティングシステムとのインターフェースを提供する重要なパッケージです。ファイルシステム操作、環境変数の取得、プロセス管理など、システムレベルの機能にアクセスするための機能が含まれています。

osパッケージの主な機能:
  • ファイルとディレクトリの操作
  • 環境変数の取得と設定
  • プロセス管理
  • 標準入出力の操作
  • プラットフォーム固有の情報取得

基本的な定数と変数

[編集]

標準入出力

[編集]
package main

import (
	"fmt"
	"os"
)

func main() {
	// 標準入力、標準出力、標準エラー出力
	fmt.Fprintln(os.Stdout, "標準出力への出力")
	fmt.Fprintln(os.Stderr, "標準エラー出力への出力")

	// ファイル記述子
	fmt.Printf("標準入力のファイル記述子: %d\n", os.Stdin.Fd())
	fmt.Printf("標準出力のファイル記述子: %d\n", os.Stdout.Fd())
	fmt.Printf("標準エラー出力のファイル記述子: %d\n", os.Stderr.Fd())
}

パス区切り文字

[編集]
package main

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

func main() {
	// プラットフォーム依存のパス区切り文字
	fmt.Printf("パス区切り文字: %c\n", os.PathSeparator)
	fmt.Printf("パスリスト区切り文字: %c\n", os.PathListSeparator)

	// 実際の使用例
	path := filepath.Join("home", "user", "documents", "file.txt")
	fmt.Printf("結合されたパス: %s\n", path)
}

ファイル操作

[編集]

ファイルの作成、開く、閉じる

[編集]
package main

import (
	"fmt"
	"os"
)

func main() {
	// ファイルの作成
	if file, err := os.Create("example.txt"); err != nil {
		fmt.Printf("ファイル作成エラー: %v\n", err)
		return
	} else {
		defer file.Close()

		// ファイルへの書き込み
		if _, err := file.WriteString("Hello, World!\n"); err != nil {
			fmt.Printf("書き込みエラー: %v\n", err)
			return
		}
	}

	// ファイルを読み取り専用で開く
	if file, err := os.Open("example.txt"); err != nil {
		fmt.Printf("ファイル読み取りエラー: %v\n", err)
		return
	} else {
		defer file.Close()

		// ファイルからの読み取り
		buffer := make([]byte, 100)
		if n, err := file.Read(buffer); err != nil {
			fmt.Printf("読み取りエラー: %v\n", err)
			return
		} else {
			fmt.Printf("読み取ったデータ: %s", string(buffer[:n]))
		}

		// ファイルの削除
		if err := os.Remove("example.txt"); err != nil {
			fmt.Printf("ファイル削除エラー: %v\n", err)
		}
	}
}

OpenFileによる詳細な制御

[編集]
package main

import (
	"fmt"
	"os"
)

func main() {
	// OpenFileを使用した詳細な制御
	if file, err := os.OpenFile("data.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644); err != nil {
		fmt.Printf("ファイル開けませんでした: %v\n", err)
		return
	} else {
		defer file.Close()

		// ファイルに追記
		if _, err = file.WriteString("追加されたデータ\n"); err != nil {
			fmt.Printf("書き込みエラー: %v\n", err)
			return
		} else {
			fmt.Println("ファイルに正常に書き込みました")
		}
	}
	// クリーンアップ
	os.Remove("data.txt")
}

ファイルフラグ

[編集]
フラグ 説明
`os.O_RDONLY` 読み取り専用
`os.O_WRONLY` 書き込み専用
`os.O_RDWR` 読み書き可能
`os.O_APPEND` 追記モード
`os.O_CREATE` ファイルが存在しない場合作成
`os.O_EXCL` O_CREATEと組み合わせて、既存ファイルの場合エラー
`os.O_SYNC` 同期書き込み
`os.O_TRUNC` ファイルを切り詰める

ファイル情報の取得

[編集]

Statによるファイル情報取得

[編集]
package main

import (
	"fmt"
	"os"
)

func main() {
	// テスト用ファイル作成
	if file, err := os.Create("test_file.txt"); err != nil {
		fmt.Printf("ファイル作成エラー: %v\n", err)
		return
	} else {
		defer file.Close()
		file.WriteString("テストデータ")
	}

	// ファイル情報の取得
	if fileInfo, err := os.Stat("test_file.txt"); err != nil {
		fmt.Printf("ファイル情報取得エラー: %v\n", err)
		return
	} else {
		fmt.Printf("ファイル名: %s\n", fileInfo.Name())
		fmt.Printf("サイズ: %d バイト\n", fileInfo.Size())
		fmt.Printf("モード: %v\n", fileInfo.Mode())
		fmt.Printf("更新日時: %v\n", fileInfo.ModTime())
		fmt.Printf("ディレクトリかどうか: %v\n", fileInfo.IsDir())
	}

	// ファイル存在チェック
	if _, err := os.Stat("存在しないファイル.txt"); os.IsNotExist(err) {
		fmt.Println("ファイルは存在しません", err)
	}

	// クリーンアップ
	os.Remove("test_file.txt")
}

ファイルの権限とモード

[編集]
package main

import (
	"fmt"
	"os"
)

func main() {
	// ファイル作成時の権限設定
	if file, err := os.OpenFile("permission_test.txt", os.O_CREATE|os.O_WRONLY, 0644); err != nil {
		fmt.Printf("ファイル作成エラー: %v\n", err)
		return
	} else {
		defer file.Close()
	}

	// 権限の変更
	if err := os.Chmod("permission_test.txt", 0755); err != nil {
		fmt.Printf("権限変更エラー: %v\n", err)
		return
	}

	// 権限の確認
	if fileInfo, err := os.Stat("permission_test.txt"); err != nil {
		fmt.Printf("ファイル情報取得エラー: %v\n", err)
		return
	} else {
		fmt.Printf("ファイル権限: %o\n", fileInfo.Mode().Perm())
	}
	
	// クリーンアップ
	os.Remove("permission_test.txt")
}

ディレクトリ操作

[編集]

ディレクトリの作成と削除

[編集]
package main

import (
	"fmt"
	"os"
)

func main() {
	// ディレクトリの作成
	if err := os.Mkdir("test_dir", 0755); err != nil {
		fmt.Printf("ディレクトリ作成エラー: %v\n", err)
		return
	}

	// 階層ディレクトリの作成
	if err := os.MkdirAll("parent/child/grandchild", 0755); err != nil {
		fmt.Printf("階層ディレクトリ作成エラー: %v\n", err)
		return
	}

	fmt.Println("ディレクトリが正常に作成されました")

	// ディレクトリの削除
	if err := os.Remove("test_dir"); err != nil {
		fmt.Printf("ディレクトリ削除エラー: %v\n", err)
	}

	// 階層ディレクトリの削除
	if err := os.RemoveAll("parent"); err != nil {
		fmt.Printf("階層ディレクトリ削除エラー: %v\n", err)
	}

	fmt.Println("ディレクトリが正常に削除されました")
}

ディレクトリの内容読み取り

[編集]
package main

import (
	"fmt"
	"os"
)

func main() {
	// テスト用ディレクトリとファイルの作成
	os.Mkdir("test_dir", 0755)
	file1, _ := os.Create("test_dir/file1.txt")
	file1.Close()
	file2, _ := os.Create("test_dir/file2.txt")
	file2.Close()
	os.Mkdir("test_dir/subdir", 0755)

	// ディレクトリの内容読み取り
	if entries, err := os.ReadDir("test_dir"); err != nil {
		fmt.Printf("ディレクトリ読み取りエラー: %v\n", err)
		return
	} else {
		fmt.Println("ディレクトリの内容:")
		for _, entry := range entries {
			if entry.IsDir() {
				fmt.Printf("  [DIR]  %s\n", entry.Name())
			} else {
				fmt.Printf("  [FILE] %s\n", entry.Name())
			}
		}
	}

	// クリーンアップ
	os.RemoveAll("test_dir")
}

環境変数

[編集]

環境変数の取得と設定

[編集]
package main

import (
	"fmt"
	"os"
)

func main() {
	// 環境変数の取得
	path := os.Getenv("PATH")
	fmt.Printf("PATH環境変数: %s\n", path)

	// 環境変数の存在確認
	if home, exists := os.LookupEnv("HOME"); exists {
		fmt.Printf("HOME環境変数: %s\n", home)
	} else {
		fmt.Println("HOME環境変数は設定されていません")
	}

	// 環境変数の設定
	if err := os.Setenv("MY_VAR", "test_value"); err != nil {
		fmt.Printf("環境変数設定エラー: %v\n", err)
		return
	}

	// 設定した環境変数の取得
	myVar := os.Getenv("MY_VAR")
	fmt.Printf("MY_VAR: %s\n", myVar)

	// 環境変数の削除
	if err := os.Unsetenv("MY_VAR"); err != nil {
		fmt.Printf("環境変数削除エラー: %v\n", err)
	}

	// 全環境変数の取得
	fmt.Println("\n全環境変数:")
	for i, env := range os.Environ() {
		if i < 5 { // 最初の5つだけ表示
			fmt.Printf("  %s\n", env)
		}
	}
	fmt.Println("  ... (省略)")
}

環境変数を使用した設定管理

[編集]
package main

import (
	"fmt"
	"os"
	"strconv"
)

type Config struct {
	Port     int
	Host     string
	Debug    bool
	Database string
}

func loadConfig() *Config {
	config := &Config{
		Port:     8080,              // デフォルト値
		Host:     "localhost",       // デフォルト値
		Debug:    false,             // デフォルト値
		Database: "sqlite://app.db", // デフォルト値
	}

	// 環境変数から設定を読み込み
	if portStr := os.Getenv("PORT"); portStr != "" {
		if port, err := strconv.Atoi(portStr); err == nil {
			config.Port = port
		}
	}

	if host := os.Getenv("HOST"); host != "" {
		config.Host = host
	}

	if debugStr := os.Getenv("DEBUG"); debugStr != "" {
		if debug, err := strconv.ParseBool(debugStr); err == nil {
			config.Debug = debug
		}
	}

	if database := os.Getenv("DATABASE_URL"); database != "" {
		config.Database = database
	}

	return config
}

func main() {
	// 環境変数の設定(テスト用)
	os.Setenv("PORT", "9000")
	os.Setenv("DEBUG", "true")

	config := loadConfig()
	fmt.Printf("設定:\n")
	fmt.Printf("  Port: %d\n", config.Port)
	fmt.Printf("  Host: %s\n", config.Host)
	fmt.Printf("  Debug: %v\n", config.Debug)
	fmt.Printf("  Database: %s\n", config.Database)
}

プロセス管理

[編集]

プロセス情報の取得

[編集]
package main

import (
	"fmt"
	"os"
)

func main() {
	// プロセスID
	pid := os.Getpid()
	fmt.Printf("現在のプロセスID: %d\n", pid)

	// 親プロセスID
	ppid := os.Getppid()
	fmt.Printf("親プロセスID: %d\n", ppid)

	// ユーザーID
	uid := os.Getuid()
	fmt.Printf("ユーザーID: %d\n", uid)

	// 実効ユーザーID
	euid := os.Geteuid()
	fmt.Printf("実効ユーザーID: %d\n", euid)

	// グループID
	gid := os.Getgid()
	fmt.Printf("グループID: %d\n", gid)

	// 実効グループID
	egid := os.Getegid()
	fmt.Printf("実効グループID: %d\n", egid)

	// 作業ディレクトリ
	if wd, err := os.Getwd(); err != nil {
		fmt.Printf("作業ディレクトリ取得エラー: %v\n", err)
	} else {
		fmt.Printf("作業ディレクトリ: %s\n", wd)
	}
}

プロセス終了

[編集]
package main

import (
	"fmt"
	"os"
	"time"
)

func main() {
	fmt.Println("プログラム開始")

	// 5秒後に終了
	go func() {
		time.Sleep(5 * time.Second)
		fmt.Println("5秒が経過しました。プログラムを終了します。")
		os.Exit(0)
	}()

	// 10秒後に異常終了
	go func() {
		time.Sleep(10 * time.Second)
		fmt.Println("10秒が経過しました。異常終了します。")
		os.Exit(1)
	}()

	// メインループ
	for {
		fmt.Println("実行中...")
		time.Sleep(1 * time.Second)
	}
}

コマンドライン引数

[編集]

基本的な引数処理

[編集]
package main

import (
	"fmt"
	"os"
)

func main() {
	// プログラム名
	fmt.Printf("プログラム名: %s\n", os.Args[0])

	// 引数の数
	fmt.Printf("引数の数: %d\n", len(os.Args)-1)

	// 全引数の表示
	fmt.Println("全引数:")
	for i, arg := range os.Args {
		fmt.Printf("  Args[%d]: %s\n", i, arg)
	}

	// 引数の処理例
	if len(os.Args) < 2 {
		fmt.Println("使用方法: program <name> [age]")
		os.Exit(1)
	}

	name := os.Args[1]
	fmt.Printf("名前: %s\n", name)

	if len(os.Args) >= 3 {
		age := os.Args[2]
		fmt.Printf("年齢: %s\n", age)
	}
}

フラグの処理

[編集]
package main

import (
	"fmt"
	"os"
	"strings"
)

func main() {
	var verbose bool
	var output string
	var files []string

	// 簡単なフラグ処理
	args := os.Args[1:]

	for i, arg := range args {
		if arg == "-v" || arg == "--verbose" {
			verbose = true
		} else if arg == "-o" || arg == "--output" {
			if i+1 < len(args) {
				output = args[i+1]
			}
		} else if !strings.HasPrefix(arg, "-") && arg != output {
			files = append(files, arg)
		}
	}

	fmt.Printf("Verbose: %v\n", verbose)
	fmt.Printf("Output: %s\n", output)
	fmt.Printf("Files: %v\n", files)
}

一時ファイルとディレクトリ

[編集]

一時ファイルの作成

[編集]
package main

import (
	"fmt"
	"os"
)

func main() {
	// 一時ファイルの作成
	if tempFile, err := os.CreateTemp("", "example_*.txt"); err != nil {
		fmt.Printf("一時ファイル作成エラー: %v\n", err)
		return
	} else {
		defer os.Remove(tempFile.Name()) // クリーンアップ
		defer tempFile.Close()

		fmt.Printf("一時ファイル: %s\n", tempFile.Name())

		// 一時ファイルへの書き込み
		_, err = tempFile.WriteString("一時的なデータ")
		if err != nil {
			fmt.Printf("書き込みエラー: %v\n", err)
			return
		}
	}

	// 一時ディレクトリの作成
	if tempDir, err := os.MkdirTemp("", "example_dir_*"); err != nil {
		fmt.Printf("一時ディレクトリ作成エラー: %v\n", err)
		return
	} else {
		defer os.RemoveAll(tempDir) // クリーンアップ

		fmt.Printf("一時ディレクトリ: %s\n", tempDir)
	}
}

エラーハンドリング

[編集]

osパッケージ固有のエラー

[編集]
package main

import (
	"fmt"
	"os"
)

func main() {
	// ファイルが存在しない場合
	if _, err := os.Open("存在しないファイル.txt"); err != nil {
		if os.IsNotExist(err) {
			fmt.Println("ファイルが存在しません")
		} else {
			fmt.Printf("その他のエラー: %v\n", err)
		}
	}

	// 権限エラー
	if err := os.Mkdir("/root/test", 0755); err != nil {
		if os.IsPermission(err) {
			fmt.Println("権限がありません")
		} else {
			fmt.Printf("その他のエラー: %v\n", err)
		}
	}

	// ファイルが既に存在する場合
	if file, err := os.OpenFile("test.txt", os.O_CREATE|os.O_EXCL, 0644); err != nil {
		if os.IsExist(err) {
			fmt.Println("ファイルが既に存在します")
		} else {
			fmt.Printf("その他のエラー: %v\n", err)
		}
	} else {
		file.Close()
		os.Remove("test.txt")
	}
}

実践的な使用例

[編集]

ファイル操作ユーティリティ

[編集]
package main

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

func copyFile(src, dst string) error {
	// ソースファイルを開く
	if srcFile, err := os.Open(src); err != nil {
		return err
	} else {
		defer srcFile.Close()

		// 宛先ファイルを作成
		if dstFile, err := os.Create(dst); err != nil {
			return err
		} else {
			defer dstFile.Close()

			// ファイルをコピー
			_, err = dstFile.ReadFrom(srcFile)
		}
	}
	return err
}

func listFiles(dir string) error {
	return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			fmt.Printf("[DIR]  %s\n", path)
		} else {
			fmt.Printf("[FILE] %s (%d bytes)\n", path, info.Size())
		}

		return nil
	})
}

func main() {
	// テスト用ファイル作成
	testFile, _ := os.Create("original.txt")
	testFile.WriteString("テストデータ")
	testFile.Close()

	// ファイルのコピー
	if err := copyFile("original.txt", "copy.txt"); err != nil {
		fmt.Printf("コピーエラー: %v\n", err)
	} else {
		fmt.Println("ファイルが正常にコピーされました")
	}

	// ファイル一覧
	fmt.Println("\n現在のディレクトリ:")
	if err = listFiles("."); err != nil {
		fmt.Printf("ファイル一覧エラー: %v\n", err)
	}

	// クリーンアップ
	os.Remove("original.txt")
	os.Remove("copy.txt")
}

設定ファイル読み込み

[編集]
package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"
)

type AppConfig struct {
	Settings map[string]string
}

func loadConfig(filename string) (*AppConfig, error) {
	config := &AppConfig{
		Settings: make(map[string]string),
	}

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

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())

		// コメントと空行をスキップ
		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}

		// key=value形式の解析
		parts := strings.SplitN(line, "=", 2)
		if len(parts) == 2 {
			key := strings.TrimSpace(parts[0])
			value := strings.TrimSpace(parts[1])
			config.Settings[key] = value
		}
	}

	return config, scanner.Err()
}

func main() {
	// 設定ファイルの作成
	configFile, err := os.Create("app.conf")
	if err != nil {
		fmt.Printf("設定ファイル作成エラー: %v\n", err)
		return
	}

	configContent := `# アプリケーション設定
host=localhost
port=8080
debug=true
database_url=postgres://user:pass@localhost/db
`

	configFile.WriteString(configContent)
	configFile.Close()

	// 設定ファイルの読み込み
	config, err := loadConfig("app.conf")
	if err != nil {
		fmt.Printf("設定ファイル読み込みエラー: %v\n", err)
		return
	}

	fmt.Println("読み込まれた設定:")
	for key, value := range config.Settings {
		fmt.Printf("  %s = %s\n", key, value)
	}

	// クリーンアップ
	os.Remove("app.conf")
}

パフォーマンス考慮事項

[編集]

ファイル操作の最適化

[編集]
package main

import (
	"bufio"
	"fmt"
	"os"
	"time"
)

func slowFileWrite(filename string, lines []string) error {
	if file, err := os.Create(filename); err != nil {
		return err
	} else {
		defer file.Close()

		for _, line := range lines {
			file.WriteString(line + "\n")
		}

		return nil
	}
}

func fastFileWrite(filename string, lines []string) error {
	if file, err := os.Create(filename); err != nil {
		return err
	} else {
		defer file.Close()

		writer := bufio.NewWriter(file)
		defer writer.Flush()

		for _, line := range lines {
			writer.WriteString(line + "\n")
		}

		return nil
	}
}

func main() {
	lines := make([]string, 10000)
	for i := range 1000 {
		lines[i] = fmt.Sprintf("Line %d", i)
	}

	// 遅い書き込み
	start := time.Now()
	slowFileWrite("slow.txt", lines)
	slowTime := time.Since(start)

	// 速い書き込み
	start = time.Now()
	fastFileWrite("fast.txt", lines)
	fastTime := time.Since(start)

	fmt.Printf("遅い書き込み: %v\n", slowTime)
	fmt.Printf("速い書き込み: %v\n", fastTime)
	fmt.Printf("速度向上: %.2fx\n", float64(slowTime)/float64(fastTime))

	// クリーンアップ
	os.Remove("slow.txt")
	os.Remove("fast.txt")
}

クロスプラットフォーム対応

[編集]

パス操作の注意点

[編集]
package main

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

func main() {
	fmt.Printf("OS: %s\n", runtime.GOOS)
	fmt.Printf("アーキテクチャ: %s\n", runtime.GOARCH)

	// パス区切り文字
	fmt.Printf("パス区切り文字: %c\n", os.PathSeparator)

	// 適切なパス結合
	path := filepath.Join("home", "user", "documents", "file.txt")
	fmt.Printf("結合されたパス: %s\n", path)

	// 絶対パス
	if absPath, err := filepath.Abs("."); err != nil {
		fmt.Printf("絶対パス取得エラー: %v\n", err)
	} else {
		fmt.Printf("絶対パス: %s\n", absPath)
	}

	// ホームディレクトリ
	if home, err := os.UserHomeDir(); err != nil {
		fmt.Printf("ホームディレクトリ取得エラー: %v\n", err)
	} else {
		fmt.Printf("ホームディレクトリ: %s\n", home)
	}

	// 一時ディレクトリ
	tempDir := os.TempDir()
	fmt.Printf("一時ディレクトリ: %s\n", tempDir)
}

プラットフォーム固有の操作

[編集]
package main

import (
	"fmt"
	"os"
	"runtime"
)

func getPlatformInfo() {
	switch runtime.GOOS {
	case "windows":
		fmt.Println("Windows環境で実行中")
		// Windows固有の処理
		if userProfile := os.Getenv("USERPROFILE"); userProfile != "" {
			fmt.Printf("ユーザープロファイル: %s\n", userProfile)
		}
	case "darwin":
		fmt.Println("macOS環境で実行中")
		// macOS固有の処理
		if user := os.Getenv("USER"); user != "" {
			fmt.Printf("ユーザー: %s\n", user)
		}
	case "freebsd":
		fmt.Println("freebsd環境で実行中")
		// macOS固有の処理
		if user := os.Getenv("USER"); user != "" {
			fmt.Printf("ユーザー: %s\n", user)
		}
	case "linux":
		fmt.Println("Linux環境で実行中")
		// Linux固有の処理
		if user := os.Getenv("USER"); user != "" {
			fmt.Printf("ユーザー: %s\n", user)
		}
	default:
		fmt.Printf("未対応のOS: %s\n", runtime.GOOS)
	}
}

func main() {
	getPlatformInfo()
}

セキュリティ考慮事項

[編集]

安全なファイル操作

[編集]
package main

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

func safeFileOperation(userPath string, baseDir string) error {
    // パスの正規化
    cleanPath := filepath.Clean(userPath)
    
    // ディレクトリトラバーサル攻撃の防止
    if strings.Contains(cleanPath, "..") {
        return fmt.Errorf("不正なパス: %s", cleanPath)
    }
    
    // ベースディレクトリ外へのアクセス防止
    fullPath := filepath.Join(baseDir, cleanPath)
    absPath, err := filepath.Abs(fullPath)
    if err != nil {
        return err
    }
    
    absBaseDir, err := filepath.Abs(baseDir)
    if err != nil {
        return err
    }
    
    if !strings.HasPrefix(absPath, absBaseDir) {
        return fmt.Errorf("ベースディレクトリ外へのアクセス: %s", absPath)
    }
    
    // 安全な操作
    fmt.Printf("安全なパス: %s\n", absPath)
    return nil
}

func main() {
    baseDir := "/tmp/safe_area"
    os.MkdirAll(baseDir, 0755)
    
    // 安全なパス
    err := safeFileOperation("subdir/file.txt", baseDir)
    if err != nil {
        fmt.Printf("エラー: %v\n", err)
    }
    
    // 危険なパス
    err = safeFileOperation("../../../etc/passwd", baseDir)
    if err != nil {
        fmt.Printf("エラー: %v\n", err)
    }
    
    // クリーンアップ
    os.RemoveAll(baseDir)
}

権限の確認

[編集]
package main

import (
    "fmt"
    "os"
)

func checkPermissions(filename string) {
    info, err := os.Stat(filename)
    if err != nil {
        fmt.Printf("ファイル情報取得エラー: %v\n", err)
        return
    }
    
    mode := info.Mode()
    
    fmt.Printf("ファイル: %s\n", filename)
    fmt.Printf("権限: %o\n", mode.Perm())
    fmt.Printf("所有者読み取り: %v\n", mode&0400 != 0)
    fmt.Printf("所有者書き込み: %v\n", mode&0200 != 0)
    fmt.Printf("所有者実行: %v\n", mode&0100 != 0)
    fmt.Printf("グループ読み取り: %v\n", mode&0040 != 0)
    fmt.Printf("グループ書き込み: %v\n", mode&0020 != 0)
    fmt.Printf("グループ実行: %v\n", mode&0010 != 0)
    fmt.Printf("その他読み取り: %v\n", mode&0004 != 0)
    fmt.Printf("その他書き込み: %v\n", mode&0002 != 0)
    fmt.Printf("その他実行: %v\n", mode&0001 != 0)
}

func main() {
    // テストファイル作成
    file, err := os.Create("permission_test.txt")
    if err != nil {
        fmt.Printf("ファイル作成エラー: %v\n", err)
        return
    }
    file.Close()
    
    // 権限設定
    os.Chmod("permission_test.txt", 0644)
    
    // 権限確認
    checkPermissions("permission_test.txt")
    
    // クリーンアップ
    os.Remove("permission_test.txt")
}

シグナルハンドリング

[編集]

基本的なシグナル処理

[編集]
package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
    "time"
)

func main() {
    // シグナルチャンネルの作成
    sigChan := make(chan os.Signal, 1)
    
    // 受信するシグナルの登録
    signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
    
    fmt.Println("プログラム開始 (Ctrl+Cで終了)")
    
    // メインループ
    go func() {
        for {
            fmt.Println("実行中...")
            time.Sleep(1 * time.Second)
        }
    }()
    
    // シグナル待機
    sig := <-sigChan
    fmt.Printf("\nシグナル %v を受信しました\n", sig)
    
    // クリーンアップ処理
    fmt.Println("クリーンアップ中...")
    time.Sleep(2 * time.Second)
    
    fmt.Println("プログラム終了")
}

グレースフルシャットダウン

[編集]
package main

import (
    "fmt"
    "os"
    "os/signal"
    "sync"
    "syscall"
    "time"
)

type Server struct {
    running bool
    wg      sync.WaitGroup
}

func (s *Server) Start() {
    s.running = true
    
    // ワーカーを開始
    for i := 0; i < 3; i++ {
        s.wg.Add(1)
        go s.worker(i)
    }
    
    fmt.Println("サーバー開始")
}

func (s *Server) Stop() {
    fmt.Println("サーバー停止中...")
    s.running = false
    s.wg.Wait()
    fmt.Println("サーバー停止完了")
}

func (s *Server) worker(id int) {
    defer s.wg.Done()
    
    for s.running {
        fmt.Printf("ワーカー %d が実行中\n", id)
        time.Sleep(1 * time.Second)
    }
    
    fmt.Printf("ワーカー %d が停止\n", id)
}

func main() {
    server := &Server{}
    
    // シグナルハンドラーの設定
    sigChan := make(chan os.Signal, 1)
    signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
    
    // サーバー開始
    server.Start()
    
    // シグナル待機
    <-sigChan
    
    // グレースフルシャットダウン
    server.Stop()
}

ログ出力の実装

[編集]

カスタムログ出力

[編集]
package main

import (
    "fmt"
    "os"
    "time"
)

type Logger struct {
    file *os.File
}

func NewLogger(filename string) (*Logger, error) {
    file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
    if err != nil {
        return nil, err
    }
    
    return &Logger{file: file}, nil
}

func (l *Logger) Close() error {
    if l.file != nil {
        return l.file.Close()
    }
    return nil
}

func (l *Logger) log(level, message string) {
    timestamp := time.Now().Format("2006-01-02 15:04:05")
    logEntry := fmt.Sprintf("[%s] %s: %s\n", timestamp, level, message)
    
    // ファイルに出力
    if l.file != nil {
        l.file.WriteString(logEntry)
    }
    
    // 標準出力にも出力
    fmt.Print(logEntry)
}

func (l *Logger) Info(message string) {
    l.log("INFO", message)
}

func (l *Logger) Error(message string) {
    l.log("ERROR", message)
}

func (l *Logger) Debug(message string) {
    l.log("DEBUG", message)
}

func main() {
    logger, err := NewLogger("app.log")
    if err != nil {
        fmt.Printf("ログファイル作成エラー: %v\n", err)
        return
    }
    defer logger.Close()
    
    logger.Info("アプリケーション開始")
    logger.Debug("デバッグ情報")
    logger.Error("エラーが発生しました")
    logger.Info("アプリケーション終了")
    
    // ログファイルの内容確認
    content, err := os.ReadFile("app.log")
    if err != nil {
        fmt.Printf("ログファイル読み取りエラー: %v\n", err)
        return
    }
    
    fmt.Println("\nログファイルの内容:")
    fmt.Print(string(content))
    
    // クリーンアップ
    os.Remove("app.log")
}

まとめ

[編集]

osパッケージは、Go言語においてオペレーティングシステムとの重要なインターフェースを提供します。ファイル操作、環境変数、プロセス管理など、システムレベルの機能を安全かつ効率的に使用するための基盤となります。

重要なポイント

[編集]
  1. エラーハンドリング: osパッケージの関数は多くの場合エラーを返すため、適切なエラーハンドリングが不可欠です
  2. リソース管理: ファイルハンドルなどのリソースは必ずクローズし、deferを使用してリソースリークを防ぐ
  3. セキュリティ: ファイルパスの検証、権限チェック、入力値の検証を行う
  4. クロスプラットフォーム: path/filepathパッケージを使用してプラットフォーム固有の問題を回避
  5. パフォーマンス: バッファリングを使用して大量のファイル操作を効率化

主要な機能

[編集]
  • ファイル操作: Create、Open、Remove、Stat など
  • ディレクトリ操作: Mkdir、ReadDir、RemoveAll など
  • 環境変数: Getenv、Setenv、Environ など
  • プロセス管理: Getpid、Exit、シグナルハンドリング
  • 一時ファイル: CreateTemp、MkdirTemp など

次の章では、Go言語のerrors パッケージについて詳しく学習します。

練習問題

[編集]
  1. 指定されたディレクトリ内のファイルサイズの合計を計算するプログラムを作成してください。
  2. 環境変数から設定を読み込み、デフォルト値を設定する設定管理システムを実装してください。
  3. ログファイルをローテーションする機能(一定サイズを超えたら新しいファイルを作成)を実装してください。
  4. コマンドライン引数を解析して、指定されたファイルをコピーするプログラムを作成してください。
  5. シグナルハンドリングを使用して、プログラムが安全に終了できるようなサーバーアプリケーションを作成してください。