os 操作系统接口
我们知道Linux操作系统的接口遵循POSIX规范,而Windows则自成一派,如果使用C/C++获取一些系统信息或是执行一些操作系统指令,在Linux下和Windows下的写法是不同的,因此大多数跨平台的编程语言标准库会对其进行统一的封装。Go语言中,我们可以使用os包进行跨平台的操作系统接口调用。这篇笔记我们简单介绍os包的使用。
文件系统操作
os包可用于文件和目录的创建、删除、移动、重命名等操作,也可以获取文件或目录的属性,以及对文件进行读写。
操作文件和文件夹
os包能够对文件和文件夹进行各种操作,比如重命名、删除、移动、修改权限等。
package main
import (
"fmt"
"os"
)
func main() {
// 重命名文件
err := os.Rename("D:\\1.txt", "D:\\1.csv")
if err != nil {
fmt.Println(err)
return
}
// 移动文件
err = os.Rename("D:\\2.txt", "D:\\workspace-go\\2.txt")
if err != nil {
fmt.Println(err)
return
}
// 删除文件
err = os.Remove("D:\\3.txt")
if err != nil {
fmt.Println(err)
return
}
}
以上操作既可以用于文件也可以用于文件夹,这里注意Rename()方法同时兼具重命名和移动文件的功能。除此之外,对文件和文件夹的操作还有很多,这里就不逐一介绍了,都比较简单,具体参考文档即可。
获取文件属性
Stat()方法返回一个FileInfo结构体,其中包含了文件描述符的基本信息,包括文件名、文件大小、是否为目录等。
package main
import (
"fmt"
"os"
)
func main() {
fileInfo, err := os.Stat("data.txt")
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Name %v\n", fileInfo.Name())
fmt.Printf("Size %v\n", fileInfo.Size())
fmt.Printf("IsDir %v\n", fileInfo.IsDir())
fmt.Printf("Mode %v\n", fileInfo.Mode())
fmt.Printf("ModTime %v\n", fileInfo.ModTime())
}
运行结果:
Name data.txt
Size 17
IsDir false
Mode -rw-rw-rw-
ModTime 2023-04-08 16:56:49.3889137 +0800 CST
文件IO
Go语言中,File结构体封装了操作系统的文件描述符,它实现了io包中的Reader和Writer接口,可用于文件内容的读写。os的Open()方法会返回*File类型的指针,读写文件等操作都需要通过*File指针来实现。
打开和关闭文件描述符
下面代码演示了如何打开和关闭文件描述符。
package main
import (
"fmt"
"os"
)
func main() {
// 打开文件
file, err := os.Open("data.txt")
if err != nil {
fmt.Println(err)
return
}
// 关闭文件
defer func(file *os.File) {
err := file.Close()
if err != nil {
fmt.Println(err)
return
}
}(file)
}
这里要注意的是,代码中关闭文件描述符使用了defer关键字确保其在函数运行结束后执行,而Close()方法本身也可能出错返回error,因此defer内部实际上是一个自执行的函数。
对于打开文件,上面使用的Open()方法默认以只读方式打开文件,Open()内部其实会调另一个方法OpenFile(),我们也可以直接调这个方法并传递打开方式,实现写、追加写等功能。
file, err := os.OpenFile("data.txt", os.O_CREATE|os.O_APPEND, 0666)
代码中,os.O_CREATE表示没有文件就创建,os.O_APPEND表示追加写,0666表示文件的操作权限,对应Linux下的8进制权限位rw-,对文件进行写操作时,我们应该设置w位,因此一般都传6。
读取文件
下面例子代码中,我们以字节的方式对文件进行了读取。
package main
import (
"fmt"
"os"
)
func main() {
// 打开文件
file, err := os.Open("data.txt")
if err != nil {
fmt.Println(err)
return
}
// 获取文件大小并创建对应大小的内存空间
fileInfo, err := file.Stat()
if err != nil {
fmt.Println(err)
return
}
bytes := make([]byte, fileInfo.Size())
// 读取文件
_, err = file.Read(bytes)
if err != nil {
fmt.Println(err)
return
}
// 打印文件内容
fmt.Println(string(bytes))
// 关闭文件
defer func(file *os.File) {
err := file.Close()
if err != nil {
fmt.Println(err)
return
}
}(file)
}
代码中,我们首先打开了一个文件描述符,然后获取了其大小(字节)。紧接着我们创建了一个相同大小的[]byte数组,然后将文件内容以字节方式读取到这个数组中。最后我们将其转化为字符串并打印,然后确保关闭了文件描述符。
注意对于较小的文本文件上面代码没有问题,但如果读取的是一个较大的二进制文件,我们对其进行操作时显然不可能将其一次性全部读入内存,此时一般都是采用循环的方式读取,Read()方法在读取后会返回当前读取了多少字节,同时也会修改当前读取的偏移量,这和大多数其它语言的标准库实现相同。此外,对于大文件我们可能还会更倾向于使用bufio来操作,它提供了内置的缓冲区,可以减少IO过程中系统调用的次数,提高读写效率。
写入数据到文件
下面代码演示了如何写入数据到文件中。
package main
import (
"fmt"
"os"
)
func main() {
// 打开文件
file, err := os.OpenFile("data.txt", os.O_RDWR|os.O_TRUNC, 0666)
if err != nil {
fmt.Println(err)
return
}
// 写入数据
_, err = file.Write([]byte("Hello, world!"))
if err != nil {
fmt.Println(err)
return
}
// 关闭文件
defer func(file *os.File) {
err := file.Close()
if err != nil {
fmt.Println(err)
return
}
}(file)
}
代码中,我们将一个字符串转换为了[]byte数组形式,然后使用Write()方法将其写入文件。注意我们打开文件时指定了os.O_RDWR|os.O_TRUNC模式,表示文件内部会先被清空,然后写入数据。
读写环境变量
os包中,Environ()方法用于返回当前可执行程序的所有环境变量,Setenv()方法用于设置环境变量,而Getenv()方法用于获取环境变量。下面例子演示了如何使用这些方法操作环境变量。
package main
import (
"fmt"
"os"
)
func main() {
// 获取所有环境变量
envs := os.Environ()
for _, env := range envs {
fmt.Println(env)
}
// 设置环境变量
err := os.Setenv("APP_KEY", "abc123")
if err != nil {
fmt.Println(err)
return
}
// 获取环境变量
env := os.Getenv("APP_KEY")
fmt.Println(env)
}
执行外部命令
os/exec包用于创建进程并执行外部命令,下面例子中我们使用exec.Command()方法调用了curl程序。
package main
import (
"fmt"
"os/exec"
)
func main() {
cmd := exec.Command("curl", "https://www.bing.com")
output, err := cmd.CombinedOutput()
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(output))
}
代码中,Command()方法的参数为在PATH环境变量中的可执行程序以及执行时传入的命令行参数,其返回值是一个*Cmd类型的指针,我们在其上调用CombinedOutput()方法,启动curl程序并获取其在标准输出的结果作为返回值。
除此此外,os/exec还有一些其它的用法,具体可以参考SDK文档。