1. 概述

os 包提供了操作系统函数的不依赖平台的接口。失败的调用会返回错误值而非错误码。通常错误值里包含更多信息。例如,如果某个使用一个文件名的调用(如Open、Stat)失败了,打印错误时会包含该文件名,错误类型将为 *PathError ,其内部可以解包获得更多信息。

type PathError struct {Op   stringPath stringErr  error
}

PathError 记录一个错误,以及导致错误的路径。

func (e *PathError) Error() string

2. type FileInfo

type FileInfo interface {Name() string       // 文件的名字(不含扩展名)Size() int64        // 普通文件返回值表示其大小;其他文件的返回值含义各系统不同Mode() FileMode     // 文件的模式位ModTime() time.Time // 文件的修改时间IsDir() bool        // 等价于Mode().IsDir()Sys() interface{}   // 底层数据来源(可以返回nil)
}

FileInfo 用来描述一个文件对象。

func Stat(name string) (fi FileInfo, err error)

Stat 返回一个描述 name 指定的文件对象的 FileInfo 。如果指定的文件对象是一个符号链接,返回的 FileInfo 描述该符号链接指向的文件的信息,本函数会尝试跳转该链接。如果出错,返回的错误值为 *PathError 类型。

代码示例:

package mainimport ("fmt""os"
)func main() {// Stat 返回一个描述 name 指定的文件对象的 FileInfofi, err := os.Stat("./test.txt")if err != nil {fmt.Println(err)}fileName := fi.Name()fileSize := fi.Size()fileMode := fi.Mode()fileModTime := fi.ModTime()isDir := fi.IsDir()fileSys := fi.Sys()fmt.Printf("fileName is %#v\n", fileName)fmt.Printf("fileSize is %#v\n", fileSize)fmt.Printf("fileMode is %#v\n", fileMode)fmt.Printf("fileModTime is %#v\n", fileModTime)fmt.Printf("isDir is %#v\n", isDir)fmt.Printf("fileSys is %#v\n", fileSys)
}

3. type FileMode

FileModel 的方法主要用来进行判断和输出权限。

type FileMode uint32

FileMode 代表文件的模式和权限位。这些字位在所有的操作系统都有相同的含义,因此文件的信息可以在不同的操作系统之间安全的移植。不是所有的位都能用于所有的系统,唯一共有的是用于表示目录的 ModeDir 位。

函数定义 函数说明
func (m FileMode) IsDir() bool IsDir报告m是否是一个目录
func (m FileMode) IsRegular() bool IsRegular报告m是否是一个普通文件
func (m FileMode) Perm() FileMode Perm方法返回m的Unix权限位
func (m FileMode) String() string 返回m的字符串表示

使用示例:

func main() {// Stat 返回一个描述 name 指定的文件对象的 FileInfofi, err := os.Stat("./test.log")if err != nil {fmt.Println(err)}fm := fi.Mode()	// 返回 FileMode fmt.Println(fm.IsDir())     // falsefmt.Println(fm.IsRegular()) // truefmt.Println(fm.Perm())      // -rw-rw----fmt.Println(fm.String())    // -rw-rw----
}

实际文件显示:

wohu@wohu:~/GoCode/src/task$ ll
total 20
-rw-rw-r--  1 wohu wohu  250 520 09:40 main.go
-rw-rw----  1 wohu wohu    0 515 10:54 test.log

4. 主要函数

4.1 os.Hostname、Exit、Getpid、Getppid、

函数定义 函数说明
func Hostname() (name string, err error) Hostname返回内核提供的主机名。
func Environ() []string Environ返回表示环境变量的格式为"key=value"的字符串的切片拷贝
func ExpandEnv(s string) string 根据当前环境变量的值替换字符串中的 ${var} 或 $var。对未定义变量的引用将被空字符串替换
func Getenv(key string) string Getenv检索并返回名为key的环境变量的值。如果不存在该环境变量会返回空字符串,要区分空值和未设置值,请使用 LookupEnv
func LookupEnv(key string) (string, bool) 检索 key 这个键对应的环境变量的值,如果该环境变量存在,则返回对应的值(可能为空),并且布尔值为 true,否则,返回值将为空,布尔值将为 false
func Setenv(key, value string) error Setenv设置名为key的环境变量。如果出错会返回该错误
func Unsetenv(key string) error 取消设置单个环境变量
func Clearenv() Clearenv删除所有环境变量(谨慎使用)
func Exit(code int) Exit让当前程序以给出的状态码code退出。一般来说,状态码0表示成功,非0表示出错。 程序会立刻终止,defer的函数不会被执行
func Getuid() int Getuid返回调用者的用户ID
func Geteuid() int Geteuid返回调用者的有效用户ID
func Getgid() int Getgid返回调用者的组ID
func Getegid() int Getegid返回调用者的有效组ID
func Getgroups() ([]int, error) Getgroups返回调用者所属的所有用户组的组ID
func Getpid() int Getpid返回调用者所在进程的进程ID
func Getppid() int Getppid返回调用者所在进程的父进程的进程ID

使用示例:

func main() {hostname, err := os.Hostname()if err != nil {fmt.Println(err)}env := os.Environ()goroot := os.Getenv("GOROOT")name := os.Getenv("NAME")fmt.Println("name is:", name)pid := os.Getpid()ppid := os.Getppid()os.Exit(0)fmt.Println(hostname)fmt.Println(env)fmt.Println(goroot)fmt.Println(pid)fmt.Println(ppid)
}

执行:

$ NAME=wohu go run main.go
name is: wohu 

示例

host := os.ExpandEnv("127.0.0.1:$PORT")
fmt.Println(host)

它会将 $PORT 替换为 os.Getenv("PORT") 的值。

4.2 os.IsExist

func IsExist(err error) bool

返回一个布尔值说明该错误是否表示一个文件或目录已经存在。 ErrExist 和一些系统调用错误会使它返回真。

4.3 os.IsNotExist

func IsNotExist(err error) bool

返回一个布尔值说明该错误是否表示一个文件或目录不存在。 ErrNotExist 和一些系统调用错误会使它返回真。

4.4 os.IsPermission

func IsPermission(err error) bool

返回一个布尔值说明该错误是否表示因权限不足要求被拒绝。 ErrPermission 和一些系统调用错误会使它返回真

4.5 os.Getwd

func Getwd() (dir string, err error)

返回一个对应当前工作目录的根路径。如果当前目录可以经过多条路径抵达(因为硬链接), Getwd 会返回其中一个。

4.5 os.Chdir

func Chdir(dir string) error

将当前工作目录修改为 dir 指定的目录。如果出错,会返回 *PathError 底层类型的错误。

代码示例如下:

func main() {_, err := os.Stat("./test.log")if os.IsExist(err) {fmt.Println("test.log do not exist")}if os.IsNotExist(err) {fmt.Println("test.log exist")}curDir, err := os.Getwd()if err != nil {fmt.Println(err)}fmt.Println(curDir) // /home/wohu/GoCode/src/taskerr = os.Chdir("/opt/")if err != nil {fmt.Println(err)}chDir, err := os.Getwd()if err != nil {fmt.Println(err)}fmt.Println(chDir) // opt
}

4.6 os.Chmod

func Chmod(name string, mode FileMode) error

修改 name 指定的文件对象的 mode 。如果 name 指定的文件是一个符号链接,它会修改该链接的目的地文件的 mode 。如果出错,会返回 *PathError 底层类型的错误。

代码示例:

func main() {// Stat 返回一个描述 name 指定的文件对象的 FileInfofi, err := os.Stat("./test.log")if err != nil {fmt.Println(err)}fm := fi.Mode() // 返回 FileModefilePerm := fm.Perm()fmt.Println(filePerm) // 修改前权限为  -rw-rw----err = os.Chmod("./test.log", 0755)if err != nil {fmt.Println(err)}fi, err = os.Stat("./test.log")if err != nil {fmt.Println(err)}filePerm = fm.Perm()fmt.Println(filePerm) // 修改后权限为  -rwxr-xr-x
}

4.7 os.Chown

func Chown(name string, uid, gid int) error

修改 name 指定的文件对象的用户 id 和组 id 。如果 name 指定的文件是一个符号链接,它会修改该链接的目的地文件的用户 id 和组 id 。如果出错,会返回 *PathError 底层类型的错误。

4.8 os.Getgroups

func Getgroups() ([]int, error) 返回调用者属于的 group ,其和 chown 配合使用,改变文件属于的 group

代码示例:

func main() {fmt.Println(os.Getgroups())                //获取调用者属于的组  [4 24 27 30 46 108 124 1000]fmt.Println(os.Getgid())                   //获取调用者当前所在的组 1000fmt.Println(os.Chown("tmp.txt", 1000, 46)) //更改文件所在的组
}

4.9 os.Mkdir

func Mkdir(name string, perm FileMode) error

使用指定的权限和名称创建一个目录,仅适用于创建父目录存在且要创建的目录不存在的情况。如果要创建的目录已经存在,则会报错,如果出错,会返回 *PathError 底层类型的错误。

4.10 os.MkdirAll

func MkdirAll(path string, perm FileMode) error

使用指定的权限和名称创建一个目录,包括任何必要的上级目录,并返回 nil ,否则返回错误。权限位 perm 会应用在每一个被本函数创建的目录上。如果 path 指定了一个已经存在的目录, MkdirAll 不做任何操作并返回 nil

代码示例:

func main() {errMsg := os.Mkdir("./testDir", 0775)if errMsg != nil {fmt.Println("创建目录失败", errMsg)return}errMsg2 := os.MkdirAll("./a/b", 0775)if errMsg2 != nil {fmt.Println("创建目录失败", errMsg2)return}
}

4.11 os.Rename

func Rename(oldpath, newpath string) error

修改一个文件的名字,移动一个文件。可能会有一些个操作系统特定的限制。

4.12 os.Remove

func Remove(name string) error

删除 name 指定的文件或目录。如果是目录的话必须要求该目录为空才能删除,否则会报错,会返回 *PathError 底层类型的错误。

4.13 os.RemoveAll

func RemoveAll(path string) error

删除 path 指定的文件,或目录及它包含的任何下级对象。它会尝试删除所有东西,除非遇到错误并返回。如果 path 指定的对象不存在, RemoveAll 会返回 nil 而不返回错误。

示例代码如下:

func main() {err := os.Rename("a", "b")if err != nil {fmt.Println(err)}err = os.Remove("./b")if err != nil {fmt.Println(err)}err = os.RemoveAll("./b")if err != nil {fmt.Println(err)}
}

5. type file

type File struct {// 内含隐藏或非导出字段
}

File 代表一个打开的文件对象。

5.1 返回文件对象的函数

5.1.1 os.Create

func Create(name string) (file *File, err error)

Create 采用模式 0666(任何人都可读写,不可执行)创建一个名为 name 的文件,如果文件已存在会截断它(为空文件)。如果成功,返回的文件对象可用于 I/O ;对应的文件描述符具有 O_RDWR 模式。如果出错,错误底层类型是 *PathError

5.1.2 os.Open

func Open(name string) (file *File, err error)

Open 打开一个文件用于读取。如果操作成功,返回的文件对象的方法可用于读取数据;对应的文件描述符具有 O_RDONLY 模式。如果出错,错误底层类型是 *PathError

该函数只能以只读模式打开文件。换句话说,我们只能从该函数返回的File值中读取内容,而不能向它写入任何内容。

5.1.3 os.OpenFile

func OpenFile(name string, flag int, perm FileMode) (file *File, err error)

OpenFile 是一个更一般性的文件打开函数,大多数调用者都应用 OpenCreate 代替本函数。它会使用指定的选项(如 O_RDONLY 等)、指定的模式(如 0666 等)打开指定名称的文件。如果操作成功,返回的文件对象可用于 I/O 。如果出错,错误底层类型是 *PathError

这个函数其实是 os.Create 函数和 os.Open 函数的底层支持,它最为灵活。这个函数有 3 个参数,分别名为 nameflagperm 。其中的 name 指代的就是文件的路径。而 flag 参数指的则是需要施加在文件描述符之上的模式,我在前面提到的只读模式就是这里的一个可选项。在 Go 语言中,这个只读模式由常量 os.O_RDONLY 代表,它是 int 类型的。当然了,这里除了只读模式之外,还有几个别的模式可选。

5.1.4 os.NewFile

func NewFile(fd uintptr, name string) *File

NewFile 使用给出的 Unix 文件描述符和名称创建一个文件。该函数在被调用的时候,需要接受一个代表文件描述符的、uintptr 类型的值,以及一个用于表示文件名的字符串值。

注意,不要被这个函数的名称误导了,它的功能并不是创建一个新的文件,而是依据一个已经存在的文件的描述符,来新建一个包装了该文件的 File 值。

eg:像这样拿到一个包装了标准错误输出的 File值。

file3 := os.NewFile(uintptr(syscall.Stderr), "/dev/stderr")if file3 != nil {defer file3.Close()file3.WriteString("The Go language program writes the contents into stderr.\n")
}

Go 学习笔记(44)— Go 标准库之 os(获取文件状态、获取/修改文件权限、创建、删除目录和文件、获取进程ID、设置获取环境变量)-编程之家
图片来源: https://time.geekbang.org/column/article/68779

5.2 文件对象的方法

func (f *File) Name() string

Name 方法返回(提供给 Open/Create 等方法的)文件名称。

func (f *File) Stat() (fi FileInfo, err error)

Stat 返回描述文件 fFileInfo 类型值。如果出错,错误底层类型是 *PathError

func (f *File) Fd() uintptr

Fd 返回与文件 f 对应的整数类型的 Unix 文件描述符。

func (f *File) Chdir() error

Chdir 将当前工作目录修改为 ff 必须是一个目录。如果出错,错误底层类型是 *PathError

func (f *File) Chmod(mode FileMode) error

Chmod 修改文件的模式。如果出错,错误底层类型是 *PathError

func (f *File) Chown(uid, gid int) error

Chown 修改文件的用户 ID 和组 ID 。如果出错,错误底层类型是 *PathError

func (f *File) Readdir(n int) (fi []FileInfo, err error)

Readdir 读取目录 f 的内容,返回一个有 n 个成员的 []FileInfo ,这些 FileInfo 是被 Lstat 返回的,采用目录顺序。对本函数的下一次调用会返回上一次调用剩余未读取的内容的信息。

如果 n>0Readdir 函数会返回一个最多 n 个成员的切片。这时,如果 Readdir 返回一个空切片,它会返回一个非 nil 的错误说明原因。如果到达了目录 f 的结尾,返回值 err 会是 io.EOF

如果 n<=0Readdir 函数返回目录中剩余所有文件对象的 FileInfo 构成的切片。此时,如果 Readdir 调用成功(读取所有内容直到结尾),它会返回该切片和 nil 的错误值。如果在到达结尾前遇到错误,会返回之前成功读取的 FileInfo 构成的切片和该错误。

func (f *File) Readdirnames(n int) (names []string, err error)

Readdirnames 读取并返回目录 f 里面的文件的名字切片。

如果 n>0Readdirnames 返回最多 n 个名字。在这种情况下,如果 Readdirnames 返回一个空的切片,它会返回一个非空的错误来解释原因。在目录的结尾,错误为 EOF

如果 n<0Readdirnames 返回目录下所有的文件的名字,用一个切片表示。在这种情况下,如果用一个切片表示成功(读取直到目录结尾),它返回切片和一个空的错误。如果在目录结尾之前遇到了一个错误, Readdirnames 返回直到当前所读到的 names 和一个非空的错误。

func (f *File) Truncate(size int64) error

Truncate 改变文件的大小,它不会改变 I/O 的当前位置。 如果截断文件,多出的部分就会被丢弃。如果出错,错误底层类型是 *PathError

func (f *File) Read(b []byte) (n int, err error)

Read 方法从 f 中读取最多 len(b) 字节数据并写入 b 。它返回读取的字节数和可能遇到的任何错误。文件终止标志是读取 0 个字节且返回值 errio.EOF

func (f *File) ReadAt(b []byte, off int64) (n int, err error)

ReadAt 从指定的位置(相对于文件开始位置)读取 len(b) 字节数据并写入 b 。它返回读取的字节数和可能遇到的任何错误。当 n<len(b) 时,本方法总是会返回错误;如果是因为到达文件结尾,返回值 err 会是 io.EOF

func (f *File) Write(b []byte) (n int, err error)

Write 向文件中写入 len(b) 字节数据。它返回写入的字节数和可能遇到的任何错误。如果返回值 n!=len(b) ,本方法会返回一个非 nil 的错误。

func (f *File) WriteString(s string) (ret int, err error)

WriteString 类似 Write ,但接受一个字符串参数。

func (f *File) WriteAt(b []byte, off int64) (n int, err error)

WriteAt 在指定的位置(相对于文件开始位置)写入 len(b) 字节数据。它返回写入的字节数和可能遇到的任何错误。如果返回值 n!=len(b) ,本方法会返回一个非 nil 的错误。

func (f *File) Seek(offset int64, whence int) (ret int64, err error)

Seek 设置下一次读/写的位置。

6. 用于File值的操作模式

针对 File 值的操作模式主要有只读模式、只写模式和读写模式。这些模式分别由常量 os.O_RDONLYos.O_WRONLYos.O_RDWR 代表。

在我们新建或打开一个文件的时候,必须把这三个模式中的一个设定为此文件的操作模式。除此之外,我们还可以为这里的文件设置额外的操作模式,可选项如下所示。

  • os.O_APPEND 当向文件中写入内容时,把新内容追加到现有内容的后边。
  • os.O_CREATE 当给定路径上的文件不存在时,创建一个新文件。
  • os.O_EXCL 需要与 os.O_CREATE 一同使用,表示在给定的路径上不能有已存在的文件。
  • os.O_SYNC 在打开的文件之上实施同步 I/O。它会保证读写的内容总会与硬盘上的数据保持同步。
  • os.O_TRUNC 如果文件已存在,并且是常规的文件,那么就先清空其中已经存在的任何内容。

对于以上操作模式的使用,os.Create 函数和 os.Open 函数都是现成的例子。

func Create(name string) (*File, error) { return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}

os.Create 函数在调用 os.OpenFile 函数的时候,给予的操作模式是 os.O_RDWRos.O_CREATEos.O_TRUNC 的组合。

这就基本上决定了前者的行为,即:如果参数 name 代表路径之上的文件不存在,那么就新建一个,否则,先清空现存文件中的全部内容。并且,它返回的 File 值的读取方法和写入方法都是可用的。

这里需要注意,多个操作模式是通过按位或操作符|组合起来的。

func Open(name string) (*File, error) {return OpenFile(name, O_RDONLY, 0)
}

os.Open 函数的功能是:以只读模式打开已经存在的文件。其根源就是它在调用 os.OpenFile 函数的时候,只提供了一个单一的操作模式 os.O_RDONLY

7. 设定常规文件的访问权限

我们已经知道,os.OpenFile 函数的第三个参数 perm 代表的是权限模式,其类型是 os.FileMode

但实际上,os.FileMode 类型能够代表的,可远不只权限模式,它还可以代表文件模式(也可以称之为文件种类)。

由于 os.FileMode 是基于 uint32 类型的再定义类型,所以它的每个值都包含了 32 个比特位。在这 32 个比特位当中,每个比特位都有其特定的含义。

比如,如果在其最高比特位上的二进制数是1,那么该值表示的文件模式就等同于 os.ModeDir ,也就是说,相应的文件代表的是一个目录。又比如,如果其中的第 26 个比特位上的是1,那么相应的值表示的文件模式就等同于 os.ModeNamedPipe ,也就是说,那个文件代表的是一个命名管道。

实际上,在一个 os.FileMode 类型的值(以下简称 FileMode 值)中,只有最低的 9 个比特位才用于表示文件的权限。当我们拿到一个此类型的值时,可以把它和 os.ModePerm 常量的值做按位与操作。这个常量的值是0777,是一个八进制的无符号整数,其最低的 9 个比特位上都是1,而更高的 23 个比特位上都是0。所以,经过这样的按位与操作之后,我们即可得到这个 FileMode 值中所有用于表示文件权限的比特位,也就是该值所表示的权限模式。

这将会与我们调用 FileMode 值的 Perm 方法所得到的结果值是一致。在这 9 个用于表示文件权限的比特位中,每 3 个比特位为一组,共可分为 3 组。从高到低,这 3 组分别表示的是文件所有者(也就是创建这个文件的那个用户)、文件所有者所属的用户组,以及其他用户对该文件的访问权限。而对于每个组,其中的 3 个比特位从高到低分别表示读权限、写权限和执行权限。如果在其中的某个比特位上的是1,那么就意味着相应的权限开启,否则,就表示相应的权限关闭。

因此,八进制整数 0777 就表示:操作系统中的所有用户都对当前的文件有读、写和执行的权限,而八进制整数 0666 则表示:所有用户都对当前文件有读和写的权限,但都没有执行的权限。我们在调用 os.OpenFile 函数的时候,可以根据以上说明设置它的第三个参数。

但要注意,只有在新建文件的时候,这里的第三个参数值才是有效的。在其他情况下,即使我们设置了此参数,也不会对目标文件产生任何的影响。

8. os 中关于进程的操作

Go 学习笔记(44)— Go 标准库之 os(获取文件状态、获取/修改文件权限、创建、删除目录和文件、获取进程ID、设置获取环境变量)-编程之家

参考:

https://studygolang.com/pkgdoc
https://www.cnblogs.com/sunailong/p/7554013.html
https://blog.csdn.net/weiyuefei/article/details/77892871