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, but 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 ofn
,t
,%
, or@
, then anewline
character, atab
character, apercent
character, or the current file number is printed.
Is there a way to work around that? edit: (accurately and efficiently?)
Update:
Sorry, the glob *
is misleading. The arguments can contain any path.
I have a working solution that forks a stat
call for each path. I want to improve it because of the massive number of files to process.
1. What you can do (accurate but slow):
Fork a stat
command for each input path:
for p in "$@"
do
stat -nf '%.9Fm' -- "$p" &&
printf '\t%s\0' "$p"
done
2. What you can do (accurate but twisted):
In the input paths, replace each occurrence of (possibly overlapping) /././
with a single /./
, make stat
output /././\n
at the end of each record, and use awk
to substitute each /././\n
by a NUL
byte:
#!/bin/bash
shopt -s extglob
stat -nf '%.9Fm%t%N/././%n' -- "${@//\/.\/+(.\/)//./}" |
awk -F '/\\./\\./' '{
if ( NF == 2 ) {
printf "%s%c", record $1, 0
record = ""
} else
record = record $1 "\n"
}'
N.B. If you wonder why I chose /././\n
as record separator then take a look at Is it "safe" to replace each occurrence of (possibly overlapped) /./
with /
in a path?
3. What you should do (accurate & fast):
You can use the following perl
one‑liner on almost every UNIX/Linux:
LANG=C perl -MTime::HiRes=stat -e '
foreach (@ARGV) {
my @st = stat($_);
if ( @st > 0 ) {
printf "%.9f\t%s\0", $st[9], $_;
} else {
printf STDERR "stat: %s: %s\n", $_, $!;
}
}
' -- "$@"
note: for perl < 5.8.9
, remove the -MTime::HiRes=stat
from the command line.
stat
:When %N
is at the end of the format string and the filename ends with a newline
character, then its trailing newline
might get stripped:
For example:
stat -f '%N' -- $'file1\n' file2
file1
file2
For getting the output that one would expect from stat -f '%N'
you can use the -n
switch and add an explicit %n
at the end of the format string:
stat -nf '%N%n' -- $'file1\n' file2
file1
file2