2

I'm running a long string of -exec commands, and I want to pull a timestamp from the beginning of the run to use as a value for subsequent commands. Is this possible? Something like:

find . -exec NOW=$(date +%s) {} \; -exec echo $NOW."$0" {} \;
find . -exec set NOW=$(date +%s) {} \; -exec echo $NOW."$0" {} \;
find . -exec export NOW=$(date +%s) {} \; -exec echo $NOW."$0" {} \;

Those don't seem to work, but if I just use $(date +%s) instead of setting the value, I get inconsistent timestamps because each subsequent -exec is happening later in time.

What I want is a different timestamp to use for each file. For example, modifying someones suggestion, this may show my intent better:

find . -exec sh -c '
    NOW=$(date +%s); for f in "$@"; do echo "$NOW  $f First Output"; sleep 3; echo "$NOW $f Second Output"; done
' find-sh {} +

That command doesn't do what I want, but shows that the delay when acting on the first file should not change the timestamp, should output the timestamp and file name, sleep 3, and output the SAME timestamp for that file. Then I want it to create a new timestamp to use with the next file.

  • You cannot pass variables between different -exec commands, but you can write the data to a file. – Bodo Jul 28 '20 at 07:52
  • Please explain in your question what you want to achieve. Using an example may be helpful to understand your requirements. – Bodo Jul 28 '20 at 07:54
  • 1
    I think I got it now, and my answer is not valid for this, removed – pLumo Jul 28 '20 at 07:57
  • Is there a guarantee that your -exec commands will take longer than a second? If the commands run faster you will get the same timestamp for several files. – Bodo Jul 28 '20 at 08:03

3 Answers3

4

Use only one -exec together with sh -c:

find . -exec sh -c '
    NOW=$(date +%s);
    echo "$NOW $1 First Output";
    sleep 3;
    echo "$NOW $1 Second Output";
' find-sh {} \; 

As an alternative, If you for whatever reason want to stick to chained -execs, you could use a temp file, but then you need sh -c whenever you read that temp file:

find . \
  -exec sh -c 'date +%s > /tmp/now' \; \
  -exec sh -c 'echo "$(cat /tmp/now) $1 First Output"' find-sh {} \; \
  -exec sleep 3 \; \
  -exec sh -c 'echo "$(cat /tmp/now) $1 Second Output"' find-sh {} \;

The following -exec won't work, because the shell will evaluate the $(..) part before the find run:

-exec echo "$(cat /tmp/now) {} First Output" \;

PS:

You may ask, where is this find-sh from -exec sh -c '...' find-sh {} coming from. Here is the answer.

pLumo
  • 22,565
  • You got it! Thank you. I'm going to test that a bit, but I think that works. – Rob Current Jul 28 '20 at 08:04
  • Note that you will probably get the same time stamp for several files if you remove the sleep command, i.e. if the real command runs fast. – Bodo Jul 28 '20 at 08:08
3

Set NOW for each file anew, i.e. inside the for loop:

find . -exec sh -c '
   for f do
      NOW=$(date +%s)
      echo "$NOW  $f First Output"
      sleep 3
      echo "$NOW $f Second Output"
   done
' find-sh {} +
0

It's not exactly clear to me what you want to do but if you want a value of $NOW that is updated to the current epoch time at the point each file is processed you could use zsh and its $EPOCHSECONDS variable (or $EPOCHREALTIME if you want the nanoseconds as well) in the zsh/datetime module (or ${(%):-%D{%s}} without zsh/datetime, though that's less legible):

find . -exec zsh -c '
  zmodload zsh/datetime
  typeset -F SECONDS # make it float
  for file do
    start_time=$EPOCHSECONDS SECONDS=0
    print -r $start_time processing $file
processing $file

end_time=$EPOCHSECONDS duration=$SECONDS
print start=$start_time end=$end_time duration=$SECONDS

done' zsh {} +

Or since zsh can do find's job with its recursive globs:

#! /bin/zsh -

zmodload zsh/datetime typeset -F SECONDS # make it float.

for file (./*/(ND)) { start_time=$EPOCHSECONDS SECONDS=0 print -r $start_time processing $file

processing $file

end_time=$EPOCHSECONDS duration=$SECONDS print start=$start_time end=$end_time duration=$SECONDS }

In the bash shell, you can get the equivalent of start_time=$EPOCHSECONDS with printf -v start_time '%(%s)T'.