开发者

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
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜