56

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?

4
  • 1
    Most of the times, you don't know in advance how long a process will take.
    – choroba
    May 28, 2016 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, 2016 at 9:23
  • @Wildcard I did clarify in the question. Not a progressbar. It will execute commands after some intervals May 28, 2016 at 9:25
  • 2
    "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, 2016 at 9:33

5 Answers 5

62

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.

5
  • 31
    I usually use sleep 5m && foobar, so if I change my mind and ^C the sleep, the next command doesn't run. May 28, 2016 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, 2016 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, 2016 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 ). May 29, 2016 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. May 29, 2016 at 4:14
7

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'

3
  • Launching a subshell in the foreground and then exec'ing the command seems needlessly complicated. I would remove the outer parentheses and the exec. May 28, 2016 at 16:51
  • 1
    @glennjackman This way one can kill the whole procedure sending CTRL+C (for example) to the main (outermost) sub-shell.
    – Vombat
    May 28, 2016 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. May 28, 2016 at 18:48
6

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.

5
  • 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...
    – user62916
    May 28, 2016 at 9:22
  • @yeti, thanks for the hint, I did not know that.
    – Serge
    May 28, 2016 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, 2016 at 9:26
  • @Wildcard ... I did not mention sleep at all!
    – user62916
    May 28, 2016 at 10:28
  • @yeti, right you are. Thanks for the tip about SECONDS! :)
    – Wildcard
    May 28, 2016 at 16:04
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.

1

Mentioning one more approach using at command.

With commands like play and notify-send it can be used to have more visible, user friendly notifications:

  • show notification popup with message
  • play sound
# schedule notification
echo "notify-send 'Wake up!' && play ~/Music/alarm.wav" | at "now + 5 minute"

It'll look something like this:
enter image description here


Note: The at command has option to execute script file. Refer man pages for more features.
These packages may not be available by default in your distribution; so you may have to install them manually.

You must log in to answer this question.

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