35

Is there way for a bash script to know if it is running in the foreground or background, and so it can behave slightly differently in each case?

2

4 Answers 4

40

Quoting man ps:

PROCESS STATE CODES

   Here are the different values that the s, stat and state output
   specifiers (header "STAT" or "S") will display to describe the state of
   a process.
   ...
   +    is in the foreground process group

So you could perform a simple check:

case $(ps -o stat= -p $$) in
  *+*) echo "Running in foreground" ;;
  *) echo "Running in background" ;;
esac
1
  • Surely anything invoked within $(...) is itself running in the background ....? I guess the process group is the important thing though, can't deny that it works.
    – Ed Randall
    Apr 4, 2019 at 14:09
2

While you could think that checking if the shell is run in interactive mode will give the same result, in practise, this isn't. The concept looks similar but is actually different. You could run an interactive script in the background feeding input thanks to expect. Also, you could launch your script with bash with the -l argument. We cannot thus rely on the bash interactivity to check if our script is run in the background or in the foreground.

The answer from devnull is thus correct. To determine if the process is running in the foreground, the ps utility checks if the process group (pgrp) is the same as the process group id associated with the session's controlling terminal (tpgid) and adds a + sign in the output according to the process state.

Knowing that, we can definitely have a pure bash version of the test:

#!/usr/bin/env bash

IFS=$' '
retval=($(</proc/$$/stat))
IFS=$' \t\n'
if [[ "${retval[3]}" == "${retval[7]}" ]]; then
    echo "Background" > ./result.txt
else
    echo "Foreground" > ./result.txt
fi
exit

In the code above, we are outputting the result in a text file because stdout is not connected when a process is run in the background.

Please note the 4th and the 8th elements of the table respectively correspond to pgrp and tpgid (see the /proc/[pid]/stat section of the man page - man 5 proc).

2
  • 1
    Thank you. This is the only reliable way I have found to know whether or not a script can prompt a user for input-- no matter how the script is run. For example, cat myscript.sh | sudo bash and bash < myscript.sh both report not being interactive using several other methods, yet the script can still prompt for input, because it is a foreground process. Mar 30, 2022 at 19:08
  • What exactly does "pure bash version" mean here? Not spawning extra processes? or even POSIX compliant? what is the benefit over the accepted answer?
    – hoijui
    Feb 4 at 11:15
2

All the previous solutions involve spawning processes, etc.. Very, very ugly, since .bashrc is called every time a bash shell launches, hence those solutions end launching 1000's of processes.

Much cleaner is asking bash itself: bash has a predefined variable $- that has "i" if its running in an interactive shell. For example, putting this into your .bashrc is much cleaner and much cheaper and, most importantly, will always work!

case "$-" in 

    *i*) # interactive shell

    ;;
esac
3
  • WHY ARE YOU YELLING? That looks very, very ugly ... Dec 14, 2017 at 19:30
  • 1
    Simple, correct solution.
    – Ed Randall
    Apr 4, 2019 at 14:09
  • 1
    This solution does not work if you launch the script from an interactive shell.
    – ingroxd
    Dec 6, 2019 at 12:15
2

Look at the file /etc/bash.bashrc".

The line that has "$PS1". Then do a "man bash" and look for the token PS1.

[ -z "$PS1" ] && return

exits a script that is not interactive.

You must log in to answer this question.

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