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-xumask 的影响
// 实际创建权限 = 程序指定权限 & (^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() // 紧跟错误检查之后
// 使用 file2.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 不要硬编码 \\ 或 / |