Skip to content
Go
os — 操作系统交互

os — 操作系统交互

os 提供了与操作系统的交互接口:文件操作、进程管理、环境变量等。


1. 文件操作

1.1 打开文件的模式标志

标志 含义
O_RDONLY 只读(默认)
O_WRONLY 只写
O_RDWR 读写
O_CREATE 不存在则创建
O_APPEND 写入时追加到末尾
O_TRUNC 打开时清空文件
O_EXCL 与 O_CREATE 一起用,文件存在则报错(原子创建)
// 等价关系
os.Open(name)     // = OpenFile(name, O_RDONLY, 0)
os.Create(name)   // = OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)

// 追加写入
file, _ := os.OpenFile("log.txt", O_CREATE|O_WRONLY|O_APPEND, 0644)

// 原子创建(文件存在则失败)
file, _ := os.OpenFile("lock.pid", O_CREATE|O_WRONLY|O_EXCL, 0644)

1.2 文件权限 (FileMode) 详解

权限表示法

Linux 文件权限使用三组三位二进制数表示:

权限格式:  所有者(user)   所属组(group)   其他人(other)
            r  w  x        r  w  x        r  w  x
二进制:     1  1  1        1  0  1        1  0  1
八进制:       7              5              5
                      → 权限 = 0755
字符 含义 数值 文件 目录
r 读(read) 4 可读取内容 可列出文件列表
w 写(write) 2 可修改内容 可创建/删除文件
x 执行(execute) 1 可执行 可进入(cd)该目录
- rw- r-- r--    0644  普通文件:所有者可读写,其他人只读
- rwx r-x r-x    0755  可执行文件或目录:所有者可读写+执行,其他人可读+执行
- rw- --- ---    0600  仅所有者可读写(密钥文件)
d rwx r-x r-x    0755  目录:所有者可进入+写,其他人可进入

八进制计算

// 每个数字 = r(4) + w(2) + x(1)
// 7 = 4+2+1 = rwx (读+写+执行)
// 5 = 4+0+1 = r-x (读+执行,不能写)
// 4 = 4+0+0 = r-- (只读)
// 6 = 4+2+0 = rw- (读+写,不能执行)

// 三个数字分别代表:所有者(user) | 所属组(group) | 其他人(other)
os.WriteFile("public.txt", data, 0644)  // rw-r--r--
os.WriteFile("secret.key", data, 0600)  // rw-------
os.WriteFile("script.sh", data, 0755)   // rwxr-xr-x

// Go 中还支持 os.FileMode 常量(不常用,更冗长)
mode := os.FileMode(0644) | os.ModeSticky  // 额外位

额外权限位

除了基本的 rwx 九位,还有三个特殊位:

八进制值 说明
setuid 04000 以文件所有者的身份执行(如 passwd
setgid 02000 以文件所属组的身份执行;目录中新建文件继承目录所属组
sticky 01000 只有文件所有者能删除(如 /tmp 目录)
// 完整权限 = 4 位八进制数:特殊位 + 3 组 rwx
os.Mkdir("/tmp/mydir", 01777)  // sticky + rwxrwxrwx(类似 /tmp)
os.Chmod("script.sh", 04755)   // setuid + rwxr-xr-x

umask 的影响

// 实际创建权限 = 程序指定权限 & (^umask)
// umask = 0022 时:
//   程序指定 0666 → 实际 0644 (rw-r--r--)
//   程序指定 0777 → 实际 0755 (rwxr-xr-x)
//
// umask = 0002 时:
//   程序指定 0666 → 实际 0664 (rw-rw-r--)
//
// 查看 umask:终端执行 umask
// Docker 容器中: 通常是 0000 或 0022
常用权限 八进制 含义 典型场景
0644 rw-r–r– 普通数据文件 配置文件、日志文件
0755 rwxr-xr-x 可执行文件/目录 二进制、脚本、目录
0600 rw——- 私密文件 密钥、私钥、数据库文件
0660 rw-rw—- 组内共享 同组成员共享的数据文件
0700 rwx—— 私有目录/脚本 ~/.ssh/ 目录
0666 rw-rw-rw- 创建文件默认值 受 umask 影响,实际很少直接使用

🚨 陷阱:实际文件权限 = 0666 & ^umask。如果 umask 是 022,则实际权限 = 0644。在 macOS/Linux 上,umask 通常是 022;Docker 中通常是 000。

1.3 Go 1.16+ 的简洁 API

// 推荐使用(自动打开+关闭)
data, err := os.ReadFile("file.txt")           // 读取全部
err := os.WriteFile("file.txt", data, 0644)    // 写入全部
entries, err := os.ReadDir(".")                // 读取目录

💡 最佳实践:小文件(< 几 MB)直接用 os.ReadFile / os.WriteFile


2. 文件操作的规范流程

2.1 打开→检查→defer Close

file, err := os.Open("data.txt")
if err != nil {
    return err
}
defer file.Close()  // 紧跟错误检查之后

// 使用 file

2.2 处理 Close 错误

// 只读文件:Close 错误通常可忽略
defer file.Close()

// 写入文件:Close 可能返回 flush 错误
defer func() {
    if err := file.Close(); err != nil {
        log.Printf("close error: %v", err)
    }
}()

3. 目录操作

// 创建目录
os.Mkdir("mydir", 0755)           // 创建单级目录
os.MkdirAll("a/b/c", 0755)        // 创建多级目录(类似 mkdir -p)

// 删除
os.Remove("file.txt")             // 删除文件或空目录
os.RemoveAll("mydir")             // 递归删除(谨慎!)

// 遍历目录(推荐 Go 1.16+)
entries, _ := os.ReadDir(".")
for _, entry := range entries {
    fmt.Printf("%s (dir=%v)\n", entry.Name(), entry.IsDir())
}

// 遍历目录树
filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error {
    if err != nil {
        return err
    }
    fmt.Println(path)
    return nil
})

4. 环境变量与进程

// 获取
val := os.Getenv("PATH")                    // 不存在返回 ""
val, ok := os.LookupEnv("MY_VAR")           // 区分空值和未设置

// 设置
os.Setenv("APP_MODE", "production")         // 影响当前进程+子进程

// 删除
os.Unsetenv("TEMP_VAR")

// 获取所有环境变量
for _, env := range os.Environ() {
    fmt.Println(env) // "KEY=value"
}

// 命令行参数
fmt.Println(os.Args) // ["/path/to/app", "arg1", "arg2"]

// 退出(跳过 defer!)
os.Exit(1) // ← defer 不会执行

🚨 陷阱os.Exit 是立即终止,不执行 defer。如果需要清理资源,在调用 os.Exit 之前手动完成。


5. 临时文件

// 临时文件
tmpFile, _ := os.CreateTemp("", "prefix-*.txt")
defer os.Remove(tmpFile.Name())  // 清理
defer tmpFile.Close()

tmpFile.Write(data)
tmpFile.Sync() // 确保落盘

// 临时目录
tmpDir, _ := os.MkdirTemp("", "myapp-*")
defer os.RemoveAll(tmpDir)

// 系统临时目录
fmt.Println(os.TempDir()) // /tmp (Linux) 或 %TEMP% (Windows)

💡 测试中:使用 t.TempDir() 而不是手写,自动清理。


6. 🚨 常见陷阱

陷阱 说明
os.Create 会截断已有文件 追加写入用 O_APPEND
defer file.Close() 在 for 循环中 大量文件不关闭 → 文件描述符耗尽
os.Rename 跨文件系统失败 Linux 跨分区 rename 会报错
os.Exit 跳过 defer 清理逻辑写在 Exit 之前
Windows 路径分隔符 filepath.Join 不要硬编码 \\/