55

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?

Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
Aniket Bhattacharyea
  • 1,015
  • 2
  • 12
  • 20
  • 1
    Most of the times, you don't know in advance how long a process will take. – choroba May 28 '16 at 09: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 09:23
  • @Wildcard I did clarify in the question. Not a progressbar. It will execute commands after some intervals – Aniket Bhattacharyea May 28 '16 at 09: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 '16 at 09:33

5 Answers5

61

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.

Wildcard
  • 35,316
  • 26
  • 130
  • 258
  • 29
    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 02: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 02: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 04:14
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.

Wildcard
  • 35,316
  • 26
  • 130
  • 258
Serge
  • 8,371
  • 2
  • 23
  • 28
  • 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... –  May 28 '16 at 09:22
  • @yeti, thanks for the hint, I did not know that. – Serge May 28 '16 at 09: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](http://infiniteundo.com/post/25326999628/falsehoods-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 09:26
  • @Wildcard ... I did not mention `sleep` at all! –  May 28 '16 at 10:28
  • @yeti, right you are. Thanks for the tip about `SECONDS`! :) – Wildcard May 28 '16 at 16:04
6

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'

Vombat
  • 12,654
  • 13
  • 44
  • 58
  • 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. – Vombat 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.

choroba
  • 45,735
  • 7
  • 84
  • 110
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.

the Hutt
  • 111
  • 2