linux - 如何在 Linux 上用纯 Go 实现 "file -s <file>"?

标签 linux go

意图:
Go 是否具有在 Linux 上执行类似于命令 file -s <path>特殊 文件统计的功能(包或其他)

示例:

[root@localhost ~]# file /proc/uptime
/proc/uptime: empty
[root@localhost ~]# file -s /proc/uptime
/proc/uptime: ASCII text

用例:
我在/proc/* 中有一个 fileglob 文件,我需要非常快速地检测它们是否真的是空的而不是看起来是空的。

使用 os 包:

代码:

result,_ := os.Stat("/proc/uptime")
fmt.Println("Name:",result.Name()," Size:",result.Size()," Mode:",int(result.Mode()))
fmt.Printf("%q",result)

结果:

Name: uptime  Size: 0  Mode: 292
&{"uptime" '\x00' 'Ĥ' {%!q(int64=63606896088) %!q(int32=413685520) %!q(*time.Location=&{ [] [] 0 0 <nil>})} {'\x03' %!q(uint64=4026532071) '\x01' '脤' '\x00' '\x00' '\x00' '\x00' '\x00' 'Ѐ' '\x00' {%!q(int64=1471299288) %!q(int64=413685520)} {%!q(int64=1471299288) %!q(int64=413685520)} {%!q(int64=1471299288) %!q(int64=413685520)} ['\x00' '\x00' '\x00']}}

明显的解决方法:
有以下明显的解决方法。但是需要调用 bash shell 来获取文件统计信息有点过头了。

output,_ := exec.Command("bash","-c","file -s","/proc/uptime").Output()
//parse output etc...

编辑/我的实际用例:
快速确定哪些文件大小为零,而无需先读取每个文件。

file -s /cgroup/memory/lsf/<cluster>/*/tasks | <clean up commands> | uniq -c
6 /cgroup/memory/lsf/<cluster>/<jobid>/tasks: ASCII text
805 /cgroup/memory/lsf/<cluster>/<jobid>/tasks: empty

所以在这种情况下,我知道只有这 6 个作业正在运行,其余 (805) 个作业已终止。读取文件的工作方式如下:

# cat /cgroup/memory/lsf/<cluster>/<jobid>/tasks
#

# cat /cgroup/memory/lsf/<cluster>/<jobid>/tasks
12352
53455
...

最佳答案

恐怕您在这里可能会感到困惑:file 的特殊之处在于它“知道”一组启发式方法来执行其任务。

据我所知,Go 在其标准库中没有类似的东西,而且我还没有遇到过实现类似 file 功能的第 3 方包(尽管我邀请您在 http://godoc.org 上按相关关键字搜索 )

另一方面,Go 提供了对底层操作系统的系统调用接口(interface)的完全访问,所以当涉及到以 file 的方式查询操作系统时,没有什么是你不能做的去吧。

所以我建议你只获取 file 的源代码,了解它在“-s”命令行选项打开的模式下做了什么,并在你的 Go 代码中实现它。 如果您有任何问题,我们会尽力让您解决具体问题。

更新

看起来我已经设法掌握了 OP 正在努力解决的问题:一个简单的检查:

$ stat -c %s /proc/$$/status && wc -c < $_
0
849

也就是说,对 /proc 下的文件的 stat 调用显示它没有内容,但实际上从该文件读取会返回该内容。

好的,所以解决方案很简单:与其在遍历文件系统的子树时调用 os.Stat(),不如尝试从文件中读取单个字节,比如:

var buf [1]byte
f, err := os.Open(fname)
if err != nil {
    // do something, or maybe ignore.
    // A not existing file is OK to ignore
    // (the POSIX error code will be ENOENT)
    // because after the `path/filepath.Walk()` fetched an entry for
    // this file from its directory, the file might well have gone.
}
_, err = f.Read(buf[:])
if err != nil {
    if err == io.EOF {
        // OK, we failed to read 1 byte, so the file is empty.
    }
    // Otherwise, deal with the error
}
f.Close()

你可能会尝试更聪明,首先获取统计信息 (使用对 os.Stat() 的调用)来查看文件是否为常规文件— 不要尝试从套接字等读取数据。

关于linux - 如何在 Linux 上用纯 Go 实现 "file -s <file>"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38963896/

相关文章:

go - 安装gocql报错

使用 CZMQ 的 zsock_new_stream 时无法获取套接字标识

c++ - 在哪里可以找到 arm-linux-gnueabihf-ld 的联机帮助页,更不用说编写良好的联机帮助页了,其中包括 -rpath 和 -rpath-link?

linux - 如何在 Linux 上检测正在使用声卡的进程?

linux - 删除不在 repo 中的 git LFS 文件

在 Raspberry Pi4 上下载 Go Mod

linux - 使用curl调用SOAP Web服务

go - 由 fmt.Sprint(e) 在 Error 方法中产生的无限循环

go - go build 是什么意思? (去构建与去安装)

go - 从 dockerize golang 模板中的文件内容设置变量