golang index out of range

项目中需要计算Linux上的disk usage。个人在查阅相关资料后决定使用bash的df命令, 通过解析df的输出来更方便地获取disk usage。第一版代码如下:

import(  
     "fmt"
     "strconv"
     "strings"
     "os/exec"
     "log"
)

func main() {  
    out, _ := exec.Command("df").Output()
    ret, err := parseDfOutput(string(out))
    if err != nil {
        log.Println(err)
    }   
    fmt.Println(ret)
}

func parseDfOutput(out string) (float64, error) {  
    outlines := strings.Split(out, "\n")
    l := len(outlines)
    var total, used float64 = 0, 0
    for _, line := range outlines[1:l-1] {
        parsedLine := strings.Fields(line)
        t, err := strconv.ParseFloat(parsedLine[1], 64) 
        if err != nil {
            return 0, err   
        }   
        u, err := strconv.ParseFloat(parsedLine[2], 64) 
        if err != nil {
            return 0, err 
        }   
        total += t
        used += u
    }   
    return used/total, nil 
}

代码在centos 7.2上面运行没有任何问题。但是运行在docker alpine image后却报错:

/go/src/dftest # go run main.go 
panic: runtime error: index out of range

goroutine 1 [running]:  
panic(0x500060, 0xc82000a070)  
    /usr/local/go/src/runtime/panic.go:481 +0x3e6
main.parseDfOutput(0xc820098000, 0x43d, 0xe00, 0xe00, 0x0, 0x0)  
    /go/src/dftest/main.go:40 +0x321
main.GetDiskUsageInfo(0xc820010118)  
    /go/src/dftest/main.go:24 +0x212
main.main()  
    /go/src/dftest/main.go:12 +0x39
exit status 2  

关键是这句index out of range数组越界了。我在alpine中执行df结果:

/go/src/dftest # df
Filesystem           1K-blocks      Used Available Use% Mounted on  
/dev/mapper/docker-253:3-805329214-d52e8766f0fdd9a7dd12a7c05f2f26b31e8d58a3785a0045af8164bfca26a091
                      10474496    327704  10146792   3% /
tmpfs                  3954476         0   3954476   0% /dev  
tmpfs                  3954476         0   3954476   0% /sys/fs/cgroup  
/dev/mapper/centos-var
                     209612800  22256172 187356628  11% /etc/resolv.conf
/dev/mapper/centos-var
                     209612800  22256172 187356628  11% /etc/hostname
/dev/mapper/centos-var
                     209612800  22256172 187356628  11% /etc/hosts
shm                      65536         0     65536   0% /dev/shm  
/dev/mapper/centos-home
                     733644800  17485984 716158816   2% /go/src/dftest
tmpfs                  3954476         0   3954476   0% /proc/kcore  
tmpfs                  3954476         0   3954476   0% /proc/timer_list  
tmpfs                  3954476         0   3954476   0% /proc/timer_stats  
tmpfs                  3954476         0   3954476   0% /proc/sched_debug  

再修改代码逐行数据df的结果后发现,如果文件系统的映射路径过长,像:

  • /dev/mapper/docker-253:3-805329214-d52e8766f0fdd9a7dd12a7c05f2f26b31e8d58a3785a0045af8164bfca26a091
  • /dev/mapper/centos-var

在linux上显示的时候一个表格无法显示,就会默认加上\n来实现更优雅的显示效果。所以

 outlines := strings.Split(out, "\n")

这行代码会产生比预计多很多的行数,导致后面解析时数组越界。

查看df使用信息后加上个flag -P就好了。即:

out, _ := exec.Command("df").Output() --> out, _ := exec.Command("df", "-P").Output()  

完整的代码如下: