Check if bash script was invoked from a shell or another script/application
Try this:
ps -o stat= -p $PPID
If the result contains "s" (lowercase) it was either run from the command line or backgrounded from within a script. To tell those two apart:
ps -o stat= -p $$
will contain a "+" if it was not backgrounded.
Here's a table:
Run $$ $PPIDCL S+ SsCL& S Ss+Script S+ S+Script& S SScript(&) S SsScript&(&) S NULL
Where (&) means the child script was backgrounded and & means the parent script (which is what "Script" refers to) that ran it was backgrounded. CL means command line. NULL means that ps output a null and that $PPID
is "1".
From man ps
:
s is a session leader + is in the foreground process group
It should be noted that this answer is based on GNU ps
, but the man pages for BSD (including OS X) indicate similar functionality. And GNU ps
is a hybrid that includes BSD functionality, among others.
I believe that what you really want to know is whether stdout is a terminal or not. If it is then you can (almost) safely assume that it's an interactive session. Try the following snippet:
if [[ -t 1 ]]; then echo "Terminal"else echo "Not-a-terminal"fi
The [[ -t 1 ]]
command above is what checks if the file descriptor 1 (i.e. stdout) is a terminal or not.
EDIT:
Please note that this will indicate a non-terminal stdout if you pipe the output to some other program. In that case you might want a more versatile condition that will also check the standard input (file descriptor 0):
[[ -t 0 || -t 1 ]]
This is a function I adapted from another post about this topic. It matches all the parent processes up to the top against the items listed in the $shells
variable. When it is done $iscli
is set to either 0 or 1. If it is set to 0, then you know it was run from a shell or what you consider shell enough for this purpose. If it is set to 1, then you know a program was involved that is not approved. I use this for scripts I want to run in a shell and via PHP when I want different output to be provided to each.
You will of course need to call the function initially without any parameters before you need $iscli
to have a value.
function top_level_parent_pid { scriptname="${0##*/}" shells="^bash|^init|^screen|^sh|^ssh|^su|${scriptname}" pid=${1:-$$} pidname="`ps --no-heading -o %c -p ${pid}`" stat=($(</proc/${pid}/stat)) ppid=${stat[3]} ppidname="`ps --no-heading -o %c -p ${ppid}`" isclitest="`echo "${ppidname}" | grep -iv -E "${shells}"`" until [ "${ppid}" -eq "1" ] || [ "${iscli}" = "1" ]; do if [[ -n "${isclitest}" ]]; then iscli="1" else iscli="0" top_level_parent_pid ${ppid} fi done}