Take the 2-minute tour ×

I want to run a command when the user becomes inactive (the system is idle). For example:

echo "You started to be inactive."

Also, when the user becomes active again (the system is not idle anymore):

echo "You started to be active, again."

I need a shell script that will do this. Is this possible without a timer/interval? Maybe some system events?

share|improve this question
Can you go into a little bit deeper detail about what you actually want to achieve? –  Nils Mar 22 at 21:31
How do you measure idleness? Is a user watching a movie idle (because he isn't interacting with the computer)? Is a user who is logged in remotely active? –  Gilles Mar 22 at 23:30
From what I know, the system has this built-in concept: idle, active etc. Is this right? –  Ionică Bizău Mar 23 at 5:33
@IonicăBizău: Not really. Idle in the context of the system would only mean that all processes are sleeping, that is 'waiting for something'. Although this may happen quite often on a modern system and may even be the case most of the time, it does not stay that way for long as there is always something to be done and if it is updating the clock. Idleness, especially in the context of your question, is more a function of the user than the system and even than it is usually linked to a specific session. –  Adaephon Mar 25 at 8:32
@Adaephon Ok. In my concept, a system is idle when the user doesn't do any action (mouse move, click, key press) at the computer for x minutes. The system becomes active when the user make the first action: clicks a mouse button, moves it, presses a key and so on. Is it possible to detect these idle*/*active states using a shell script? –  Ionică Bizău Mar 25 at 11:33

4 Answers 4

up vote 4 down vote accepted

This thread on the ArchLinux forums contains a short C program that queries the xscreensaver for information how long the user has been idle, this seems to be quite close to your requirements:

#include <X11/extensions/scrnsaver.h>
#include <stdio.h>

int main(void) {
    Display *dpy = XOpenDisplay(NULL);

    if (!dpy) {

    XScreenSaverInfo *info = XScreenSaverAllocInfo();
    XScreenSaverQueryInfo(dpy, DefaultRootWindow(dpy), info);
    printf("%u\n", info->idle);


Save this as getIdle.c and compile with

gcc -o getIdle getIdle.c -lXss -lX11

to get an executable file getIdle. This program prints the "idle time" (user does not move/click with mouse, does not use keyboard) in milliseconds, so a bash script that builds upon this could looke like this:


idleAfter=3000     # consider idle after 3000 ms

while true; do
  echo $idleTimeMillis  # just for debug purposes.
  if [[ $idleTimeMillis -gt $idleAfter && $idle = false  ]] ; then
    echo "start idle"   # or whatever command(s) you want to run...

  if [[ $idleTimeMillis -lt $idleAfter && $idle = true ]] ; then
    echo "end idle"     # same here.
  sleep 1      # polling interval


This still needs regular polling, but it does everything you need...

share|improve this answer
Works as supposed! +1 –  Ionică Bizău Apr 3 at 12:28
You are worth of more points! –  Ionică Bizău Apr 3 at 12:35

TMOUT in bash will terminate an interactive session after the set number of seconds. You may use that mechanism.

You migth capture the logout by setting an according trap (I did not test that), or by using the bash-logout-scripts (~/.bash_logout).

Here is a good superuser-answer into that direction.

share|improve this answer
This does not address how the user would execute a command, rather than logging out, after $TMOUT seconds have passed. –  chepner Mar 23 at 3:46
Can you add more information to your answer? It is still unclear for me... –  Ionică Bizău Mar 23 at 6:39
@chepner I added some more detail to my answer. –  Nils Mar 23 at 21:53
Either way, this simply logs the user out after time is up. I don't think there is any way to abort the log out, either from an EXIT trap or from .bash_logout. –  chepner Mar 24 at 12:30

This is not quite what you asked for, but there is always the batch-command (usually a special invocation of the at-command and using the atd-daemon).

batch lets you cue-up a command to be run when the load-average drop below a certain limit (usually 1.5, but this can be set when starting atd). With at it's also possible to cue a job in such a way that rather than being run at a certain time; the job is just delivered to batchat that time, and first run when the load-average drops (eg. it's run as soon as the load-average drops under 1.5 sometime after 2:00am).

Unfortunately a batch-job will then run to it's end, it will not stop if the computer is no-longer idle.


If you have to go the programming-route (which it looks like from the other answers), I think I'd try to make something similar to the atd or crond daemons, that monitored logged-in users and/or load-average. This daemon could then run scripts/programs from a certain directory, and start/continue or stop them (with signals) as needed be.

share|improve this answer
If you did this and used .lock files you could easily handle timing issues. –  mikeserv Apr 5 at 12:02

I don't know of a way to do this without polling some sort of system stats, like the other answers use a screensaver or bash idle timer, or running from .bash_logout, but here's an idea to check CPU usage.

This would still involve polling every n seconds, and if your CPU usage is under whatever amount you choose then you can script whatever you want to run. However, whatever you run could raise the CPU usage, but you could use nice on your "stuff" to not count it.

Here's a test script using top, but you could use mpstat instead, or check load averages instead?

while true
idle=$(top -bn2 | grep "Cpu(s)"|tail -n 1|sed "s/.*, *\([0-9.]*\)%* id.*/\1/")
echo "idle is $idle"
if [[ $idle > 90 ]]
    echo "idle above 90%"
    echo "Do stuff now"
    echo "idle below 90%"
    echo "Stop doing stuff now"
sleep 1

That's just a script I threw together to test out reading the idle from top. You could parse /proc/stat but I think it only shows total times, and you'd need to compare results over an interval. Top has it's own problem for me (linux mint 16), on the first run it seems to never change cpustats, as if it has to wait to parse /proc/stat itself, hence the top -bn2 but in theory top -bn1 should work.

share|improve this answer

Your Answer


