Check if bash script was invoked from a shell or another script/application
I am writing a bash script to redirect output from another command to the proper location. Basically, when the script is invoked from a shell/commandline I want to send the output to STDOUT. But, when the bash script is executed from some other appli开发者_如何学编程cation (e.g. another bash script, some application, or in my case from the awesome-prompt plugin in my Awesome Window Manager) I want to redirect the output somewhere else.
Is there any way in bash to see how a script was invoked?
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 $$ $PPID
CL S+ Ss
CL& S Ss+
Script S+ S+
Script& S S
Script(&) S Ss
Script&(&) 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
}
精彩评论