Home > Blockchain >  How to generate a NUL-delimited stream of timestamped filenames with BSD `stat` command
How to generate a NUL-delimited stream of timestamped filenames with BSD `stat` command

Time:09-14

Let's suppose that you need to generate a NUL-delimited stream of timestamped filenames.

On Linux & Solaris I can do it with:

stat --printf '%.9Y %n\0' -- *

On BSD, I can get the same info, albeit delimited by newlines, with:

stat -f '%.9Fm %N' -- *

The man talks about a few escape sequences but the NUL byte doesn't seem supported:

If the % is immediately followed by one of n, t, %, or @, then a newline character, a tab character, a percent character, or the current file number is printed.

Is there a way to work around that?

CodePudding user response:

Unfortunately, stat out of the box does not offer this option, and so what you ask is not directly achievable.

However, you can easily implement the required functionality in a scripting language like Perl or Python.

#!/usr/bin/env python3

from pathlib import Path
from sys import argv

for arg in argv[1:]:
    print(
        Path(arg).stat().st_mtime,
        arg, end="\0")

Demo: https://ideone.com/vXiSPY

The demo exhibits a small discrepancy in the mtime which does not seem to be a rounding error, but the result could be different on MacOS (the demo platform is Debian Linux, apparently). If you want to force the result to a particular number of decimal places, Python has formatting facilities similar to those of stat and printf.

CodePudding user response:

With any command that can't produce NUL-terminated output, you can just wrap it in a function to call the command and then printf it's output with a terminating NUL instead of newline:

nulstat() {
    local fmt=$1 file
    shift
    for file in "$@"; do
        printf '%s\0' "$(stat -f "$fmt" "$file")"
    done
}

nulstat '%.9Fm %N' *

For example:

$ > foo

$ > $'foo\nbar'

$ nulstat '%.9Fm %N' foo* | od -c
0000000    1   6   6   3   1   6   2   5   3   6   .   4   7   7   9   8
0000020    0   1   4   0       f   o   o  \0   1   6   6   3   1   6   2
0000040    5   3   9   .   3   8   8   0   6   9   9   3   0       f   o
0000060    o  \n   b   a   r  \0
0000066

CodePudding user response:

Is there a way to work around that?

If all you need is to just replace all newlines with NULLs, then following tr should suffice

stat -f '%.9Fm %N' * | tr '\n' '\000'

Explanation: 000 is NULL expressed as octal value.

  • Related