So far I have this:

sudo find /path/to/dir -type f |
  xargs -d "\n" sudo stat -c "%Y %n" | 
    {arithmetic to check if %Y is between 1685518962 and 1685624474??} | 
      {show file path}

To note, I am aware of find -newermt but am more generally asking about performing arithmetic in the command line.

To clarify, I am hoping for a one-liner.

TCSH or BASH are acceptable to me but don't hesitate to enlighten me with another shell.


4 Answers 4


Use NUL-delimited records (-print0 for find, -0 for xargs) to process file lists, not lines as newline is as valid as any other character (or non-character) in a file path.

Since you're already using GNU extensions, here you can do:

sudo find /path/to/dir -type f '(' \
   -newermt @1685518962 ! -newermt @1685624474 -exec something with {} + \
   -o -exec something-else with {} + ')'

GNU find has had the ability to report metadata from files (with a much better interface) long before a GNU stat command was introduced. For post-processing you can use anything that supports NUL-delimited records and floating point arithmetic such as gawk/mawk, perl or zsh:

sudo find -H /path/to/dir -type f -printf '%T@\0%p\0' |
  LC_ALL=C gawk -v RS='\0' -F: '
    {mtime = $0; getline file}
    mtime > 1685518962 && mtime <= 1685624474 {
      # something with mtime and file

With perl:

sudo find -H /path/to/dir -type f -printf '%T@\0%p\0' | perl -0lne '
  $mtime = $_; $file = <>;
  if ($mtime > 1685518962 && $mtime <= 1685624474) {
    # something with $mtime and $file

Any code including any of the examples given here can be put on one line. If it's a golfing exercise, perl would likely be the most adapted of them:

sudo find -H /path/to/dir -type f -printf '%p\0%T@\0'|perl -0lne'$m=<>;{...}if$m>1685518962&&$m<=1685624474'

Or if using zsh, in case you need a shell to get involved in the processing of those files:

sudo find -H /path/to/dir -type f -printf '%T@/%p\0' |
  while IFS=/ read -rd '' mtime file; do
    (( mtime > 1685518962 && mtime <= 1685624474 )) &&
      something with $mtime and $file

Though zsh also has most of find's feature builtin, and a builtin stat (which predates GNU stat), which would avoid having to depend on GNUisms though would likely be slower:

sudo zsh -c '
  zmodload zsh/stat
  for file ( /path/to/dir/**/*(ND.) ) {
    stat -LF %s.%N -A mtime +mtime -- $file &&
      (( mtime > 1685518962 && mtime <= 1685624474 )) &&
      something with $file and $mtime

I wouldn't use tcsh in this day and age as it's virtually impossible to do anything reliably with it, but since you mention it, note that it does support recursive globbing (copied from zsh in 2010, shortly after bash), and has builtin support to retrieve a file's mtime, though not with subsecond precision (anyway its arithmetic is integer only) and only after symlink resolution.

set globstar globdot
foreach file ( /path/to/dir/** )
  if ( -f $file:q && ! -l $file:q ) then
    @ mtime = -M $file:q
    if ( $mtime > 1685518962 && $mtime <= 1685624474 ) then
      something with $file:q and $mtime

Note that though the other ones get the mtime with full nanosecond precision, the double floating point number type of most computers/C-compilers and as a consequence of awk/perl/zsh¹ can't hold as much precision as needed for today's timestamp.

For instance, most of those would fail to report a file last modified at 1685518962.000000001. Likely not a big problem in your case; that may become so if you needed to find files last modified within a given microsecond with high precision for instance.

Another note: because sudo passes a SUDO_COMMAND environment variable to the command it executes, and that variable contains all the arguments, that means that xargs sudo cmd is likely to fail with a "too many arguments" error if there is a large number of files as the execve() limit is on the cumulative size of arguments and environment strings, so while xargs will manage to run sudo, sudo may fail to run cmd as the cumulative size doubles inbetween.

For that reason, and also because it saves running some sudo invocations, it's better to use sudo xargs cmd. Here you could also invoke sudo only once with sudo sh -c '...'

¹ that can be worked around with -MvPREC=100 options in recent versions of gawk if arbitrary precision arithmetic has been enabled at build time, or with -Mbignum in perl, or more generally by comparing the seconds and nanoseconds separately (like -newertmt or shells/['s -nt operators likely do) or the numbers using numeric string comparisons instead of converting them to fixed size binaries, like with () [[ $1 = ${${(n)@}[1]} ]] $t1 $t2 instead of (( t1 < t2 )) in zsh using the numeric sort parameter expansion flag.


This sounds like a job for awk:

sudo find /path/to/dir -type f |
  xargs -d "\n" sudo stat -c "%Y %n" |
  awk '$1 > 1685518962 && $1 < 1685624474 {print}'

You can drop xargs by using the -execdir option to find:

sudo find /path/to/dir -type f -execdir stat -c "%Y %n" {} + |
  awk '$1 > 1685518962 && $1 < 1685624474 {print}'
  • This will fail if the file names contain newline characters, as usual. It might be safer to have something like ... stat -c "file: %Y %n" | awk '$1 == "file:" && $2 > 1683907726 && $2 < 1684446830 {print}'.
    – terdon
    Jun 1 at 13:53
  • 1
    If you use -execdir, the output will be missing the path of the directory where the files are found. Jun 1 at 14:30

Well, bash being acceptable

shopt -s dotglob globstar extglob nullglob


for file in **/*; do
  # check for regular file-ness
  modtime=$(stat -c '%Y' -- "${file}")
  [[ ( ! -L ${file} ) \
    && -f ${file} \
    && (  ${modtime} -gt ${oldest} ) \
    && (  ${modtime} -le ${newest} ) ]] \
  && {
    # do stuff

Zsh is a bit prettier in what the globbing can do without any ado

zmodload zsh/stat


for file in **/*(.ND); do
  modtime=$(zstat -L +mtime -- "${file}")
  [[  ${modtime} -gt ${oldest}  \
  &&  ${modtime} -le ${newest} ]] \
  && {
    # do stuff

Because your question literally asks for arithmetics, I'll point out that both zsh and bash support arithmetic operations in expansion: $(( ${modtime} - ${oldest} )) will give you the seconds since the lower-limit second. Zsh's arithmetics capabilities are a bit better, especially when it comes to floating point.

  • [ -f returns true for symlinks to regular files while -type f or zsh's . qualifier excludes them. Jun 1 at 14:27
  • zsh has had a stat builtin long before GNU coreutils added one. Jun 1 at 14:28
  • 1
    That check for type is done after symlink resolution. A symlink to a regular file can be seen as a regular file. It's the intention of links to provide other paths to a same file. Sometimes it's desirable to consider those as if they were the target (like stat() or [ does except with -L/-h) and sometimes not (ilke lstat()) Jun 1 at 14:37
  • 1
    stat -c is GNU stat syntax, different from zsh builtin, different from BSD stat. To enable the builtin, you need zmodload zsh/stat. In any case, if you use $(...), that's forking a process. Use the -A or -H to store the result into a variable. See my answer for an example. Jun 2 at 11:03
  • 1
    @StéphaneChazelas oh man my script has more bugs than lines at this point ;) Jun 2 at 11:17

Notwithstanding the advice of other answers, in general, you'll need to combine two commands to create a one-liner shell arithmetic filter function you can use in a pipeline:

  1. xargs ability to map over input lines, with
  2. sh to access shell arithmetic functions

in the pattern xargs -L1 sh -c '_() { ... }; _ "$@"'

To your specific example, that would look like:

find ...
  | xargs stat ...
  | xargs -L1 sh -c '_() { [ 1685518962 -le $0 -a $0 -le 1685624474 ] && echo $@; }; _ "$@"'

This works for quick and dirty tasks, like the one you have here. If you have any data volume, though, you really want to deploy a tool that has the map-reduce functionality built in, like awk.

  • Oh, random down voter, please why?
    – bishop
    Jun 7 at 14:46

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .