Will
Will

Reputation: 103

How do I implement "file -s <file>" on Linux in pure Go?

Intent:
Does Go have the functionality (package or otherwise) to perform a special file stat on Linux akin to the command file -s <path>

Example:

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

Use Case:
I have a fileglob of files in /proc/* that I need to very quickly detect if they are truly empty instead of appearing to be empty.

Using The os Package:

Code:

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

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']}}

Obvious Workaround:
There is the obvious workaround of the following. But it's a little over the top to need to call in a bash shell in order to get file stats.

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

EDIT/MY PRACTICAL USE CASE:
Quickly determining which files are zero size without needing to read each one of them first.

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

So in this case, I know that only those 6 jobs are running and the rest (805) have terminated. Reading the file works like this:

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

or

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

Upvotes: 0

Views: 163

Answers (2)

kostix
kostix

Reputation: 55483

I'm afraid you might be confusing matters here: file is special in precisely a way it "knows" a set of heuristics to carry out its tasks.

To my knowledge, Go does not have anything like this in its standard library, and I've not came across a 3rd-party package implementing a file-like functionality (though I invite you to search by relevant keywords on http://godoc.org)

On the other hand, Go provides full access to the syscall interface of the underlying OS so when it comes to querying the OS in a way file does it, there's nothing you could not do in plain Go.

So I suggest you to just fetch the source code of file, learn what it does in its mode turned on by the "-s" command-line option and implement that in your Go code. We'll try to have you with specific problems doing that — should you have any.

Update

Looks like I've managed to grasp the OP is struggling with: a simple check:

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

That is, the stat call on a file under /proc shows it has no contents but actually reading from that file returns that contents.

OK, so the solution is simple: instead of doing a call to os.Stat() while traversing the subtree of the filesystem one should instead merely attempt to read a single byte from the file, like in:

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()

You might try to be more clever and first obtain the stat information (using a call to os.Stat()) to see if the file is a regular file—to not attempt reading from sockets etc.

Upvotes: 2

I have a fileglob of files in /proc/* that I need to very quickly detect if they are truly empty instead of appearing to be empty.

They are truly empty in some sense (eg. they occupy no space on file system). If you want to check whether any data can be read from them, try reading from them - that's what file -s does:

-s, --special-files

Normally, file only attempts to read and determine the type of argument files which stat(2) reports are ordinary files. This prevents problems, because reading special files may have peculiar consequences. Specifying the -s option causes file to also read argument files which are block or character special files. This is useful for determining the filesystem types of the data in raw disk partitions, which are block special files. This option also causes file to disregard the file size as reported by stat(2) since on some systems it reports a zero size for raw disk partitions.

Upvotes: 2

Related Questions