23

If I'm executing a long process, is there any way I can execute some time-based commands?

For example, I'm running a really long process which runs for roughly 10 minutes.

After 5 minutes, I would like to run a separate command. For illustration, the separate command could be: echo 5 minutes complete

(Note: I don't want progress toward completion of the command, but simply commands executed after specified intervals.)

Is it possible?

  • 1
    Most of the times, you don't know in advance how long a process will take. – choroba May 28 '16 at 9:13
  • What do you mean "time-based commands"? Do you want to create a progress bar? Based on the current phrasing of your question, you could just put the long running command in the background and then display the system clock. Please clarify. – Wildcard May 28 '16 at 9:23
  • @Wildcard I did clarify in the question. Not a progressbar. It will execute commands after some intervals – Aniket Bhattacharyea May 28 '16 at 9:25
  • 1
    "5 minutes complete" is an odd thing to echo. Not "5 minutes have elapsed"? Not "The time is _____"? Do you just want to ensure that some specified interval of time has elapsed from the time that your long-running command was started, before another arbitrary command is run? If so, why not just run longcommand & sleep 300 && command-to-do-after-five-minutes? (Actually that is probably what you are looking for.) Note that your clarification wasn't sufficient, since you got two clock-progress-bar implementations right off the bat. – Wildcard May 28 '16 at 9:33
28

Just run:

long-command & sleep 300; do-this-after-five-minutes

The do-this-after-five-minutes will get run after five minutes. The long-command will be running in the background.

  • 10
    I usually use sleep 5m && foobar, so if I change my mind and ^C the sleep, the next command doesn't run. – Peter Cordes May 28 '16 at 14:07
  • @PeterCordes, when I tested it with e.g. sleep 3; ls and ^C, the second command doesn't run anyway. Not really sure why and maybe it works differently in a non-interactive shell? – Wildcard May 28 '16 at 16:06
  • 1
    @PeterCordes Not quite -- unless it differs per shell. When sleep 5m && echo is sleeping, when you suspend, only the sleep command is suspended. The rest of the command line runs, but since sleep got suspended, it did not exit successfully, and the part after && is skipped. Try suspending sleep 5 || echo hello, and hello shows up directly after pressing ^Z. – hvd May 29 '16 at 2:49
  • 1
    @hvd: Yup, you're right, and I'm wrong again >.<, using bash4. Apparently it's been too long since I decided that && was the way to go as a ghetto at(1), for stuff like sleep 30m && mplayer something.mp3 alarm reminder, or for sleep 90m && fg to resume a disk-intensive command later. So actually, sleep 5m && echo hello is nice because ^Z suspends sleep without running the following command at all. If you want to be able to suspend / resume the whole compound command, even before the sleep exits, use ( sleep 2 && echo hello ). – Peter Cordes May 29 '16 at 2:58
  • @hvd: The && idiom lets you be absolutely sure you don't kill the process right after sleep runs (because you can ^Z to not run the command, instead of risking a ^C). This is useful in the sleep && fg or sleep && bg use-case, when the command being resumed is part way through something slow. – Peter Cordes May 29 '16 at 4:14
5

You could use this script:

#!/bin/bash

TEMPFILE="$(mktemp)"
STARTTIME="$(date +%s)"
(./longprocess; rm -f "$TEMPFILE") &
while [ -f "$TEMPFILE" ]; do
    sleep 1s
    NOW="$(date +%s)"
    if (( (NOW-STARTTIME) % 300 == 0 )); then
        echo "$(( (NOW-STARTTIME)/60 )) minute(s) elapsed"
    fi
done
echo "Done!!!"

It executes your longprocess in a sub-shell and then monitors previously created 'lock' file for existence.

  • 2
    Check man bash for SECONDS. You can set SECONDS=0 at script start and only check for greater or equal to 300 or set SECONDS=$((date +%s)) and forget using date again in your script... – yeti May 28 '16 at 9:22
  • @yeti, thanks for the hint, I did not know that. – Serge May 28 '16 at 9:24
  • @yeti, if you want to rely on the time after sleep 1s always being exactly one second later than it was before sleep 1s...then you have fallen victim to Falsehoods that programmers believe about time. (Although in this case, you might as well just show a hash mark progress bar or something.) – Wildcard May 28 '16 at 9:26
  • @Wildcard ... I did not mention sleep at all! – yeti May 28 '16 at 10:28
  • @yeti, right you are. Thanks for the tip about SECONDS! :) – Wildcard May 28 '16 at 16:04
4

There is a one liner for this:

( ( sleep $TIMEOUT ; echo "5 minutes complete") & $COMMAND )

In your case TIMEOUT=5m and COMMAND is the long command.

Also see my answer to this post Timeout with 'service network restart'

  • Launching a subshell in the foreground and then exec'ing the command seems needlessly complicated. I would remove the outer parentheses and the exec. – glenn jackman May 28 '16 at 16:51
  • 1
    @glennjackman This way one can kill the whole procedure sending CTRL+C (for example) to the main (outermost) sub-shell. – coffeMug May 28 '16 at 18:30
  • 1
    But using exec, you don't have a subshell anymore, you just have the command. The command's parent is the shell running the script just as if you had not used a subshell at all. – glenn jackman May 28 '16 at 18:48
3
#! /bin/bash

( sleep 4 ) & # <-- The long running process.

seconds=1
while jobs %1 &>/dev/null ; do
    echo $((seconds++)) seconds complete
    sleep 1    
done
echo Done.

jobs %1 fails once the job %1 has stopped.

Note that for longer times, $seconds might get out of sync with the real time. It's better to store the start time and compute the difference to the actual time.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.