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.

4
  • You cannot pass variables between different -exec commands, but you can write the data to a file.
    – Bodo
    Jul 28, 2020 at 7:52
  • Please explain in your question what you want to achieve. Using an example may be helpful to understand your requirements.
    – Bodo
    Jul 28, 2020 at 7:54
  • 1
    I think I got it now, and my answer is not valid for this, removed
    – pLumo
    Jul 28, 2020 at 7: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, 2020 at 8:03

3 Answers 3

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.

2
  • You got it! Thank you. I'm going to test that a bit, but I think that works. Jul 28, 2020 at 8: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, 2020 at 8: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 {} +
2
  • @pLumo, + and the loop avoids forking a shell for every file.
    – ilkkachu
    Jul 28, 2020 at 8:25
  • That is indeed true -_-
    – pLumo
    Jul 28, 2020 at 8:27
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'.

You must log in to answer this question.

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