"ls" counterpart to "find" operator "-printf"?
The find
command has a handy -printf
operator that prints
user-specified metadata for each found file/folder. Is there such an
option for the ls
command?
As an alternative, I can feed the list of filenames of interest to
find
instead of ls
, but it seems like using a sledgehammer on a
fly. I already have the files of interest, and I’m not really
"finding" anything.
Additionally, feeding the paths to find
would be tricky because I
can’t just append the desired file paths to the end of a find
command. The find
command requires that the paths come before the
operators (or "predicates"). For this reason, I can’t easily leverage
"xargs".
Thanks to steeldriver for his answer. I think that his use of stat
would be a solution if I was starting from scratch. Unfortunately, I have to compare the output with similar information that has already been generated from other systems, specifically using find
‘s printf
.
Here is some code idioms using find
that I found to work:
# Option 1
Some Command
| xargs -n 1 -I{} find {} -printf '%pt%TY-%Tm-%Td %TH:%TMt%sn'
# Option 2
Some Command | tr 'n' ' '
| xargs -0 -I{} find {} -printf '%pt%TY-%Tm-%Td %TH:%TMt%sn'
Based on muru’s comment, here is a code idiom that I haven’t
gotten working because because my Gnu find
from
Cygwin is older than version 4.9 and doesn’t recognize the predicate -files0-from
:
Some Command | tr 'n' ' '
| find -files0-from - -printf '%pt%TY-%Tm-%Td %TH:%TMt%sn'
Not with the GNU implementation of ls
¹ as far as I know, however it’s possible with some implementations of stat
.
For example stat
from GNU Coreutils, stat --printf '%nt%yt%sn'
will give you the fields and delimiters that you want although it doesn’t (afaik) provide fine-grained control of the datetime format².
If zsh
is an option however, you could use the zsh/stat Module, perhaps defining your own shell function with the exact output you want:
zmodload -F zsh/stat b:zstat
mystat () {
local f s ret=0
for f do
zstat -n -LF '%Y-%m-%d %H:%M' -H s -- $f &&
printf '%st%st%sn' $s[name] $s[mtime] $s[size] ||
ret=$?
done
return $ret
}
Alternatively you may be able to do what you want with a simple perl script using its lstat function, for example
mystat() {
perl -MPOSIX -E '
foreach $f (@ARGV) {
if (@s = lstat($f)) {
say join "t", $f, strftime("%Y-%m-%d %H:%M", localtime($s[9])), $s[7];
} else {
warn "$f: $!n";
$ret = 1;
}
}
exit $ret;
' -- "$@"
}
In any case, that -printf
is specific to the GNU implementation of find
(though existed decades before a GNU stat
command was added). With GNU find
4.9 or newer, you can pass an arbitrary list of paths reliably with the -files0-from
predicate:
mystat() {
[ "$#" -eq 0 ] ||
printf '%s ' "$@" |
find -files0-from - -prune -printf '%pt%TY-%Tm-%Td %TH:%TMt%sn'
}
In any case, note that TAB and NL are as valid characters as any in a file path, so that output cannot be parsed reliably (the timestamp format which can be simplified to %F %R
is also ambiguous as it’s missing the timezone information).
All those do a lstat()
on the files, so that for those of type symlink they report the information about the symlink itself rather than the file they eventually resolve to. To do a stat()
instead (and report the information after symlink resolution), add the -L
option to GNU stat
or GNU find
, remove it from zsh stat, remove the -P
option to ast-open’s ls
, and replace lstat
with stat
in the perl
script.
¹ the ast-open implementation does though with ls -PdZ '%(name)st%(mtime:time=%Y-%m-%d %H:%M)st%(size)s' -- "$@"
(same syntax as for the standard pax
command) but AFAIK, that one is no longer being maintained.
² GNU stat
also fails on the file called -
as it interprets it as meaning to perform a fstat()
on stdin; zsh’s stat
(which predates GNU’s by a few years) doesn’t have the issue and has a -f FD
to perform a fstat()
on arbitrary file descriptors.