【问题标题】:Check if bash script was invoked from a shell or another script/application检查是否从 shell 或其他脚本/应用程序调用了 bash 脚本
【发布时间】:2011-05-14 19:06:15
【问题描述】:

我正在编写一个 bash 脚本来将另一个命令的输出重定向到正确的位置。基本上,当从 shell/命令行调用脚本时,我想将输出发送到 STDOUT。但是,当从其他应用程序执行 bash 脚本时(例如,另一个 bash 脚本、某个应用程序,或者在我的情况下是从 Awesome Window Manager 中的 awesome-prompt 插件),我想将输出重定向到其他地方。

在 bash 中有什么方法可以查看脚本是如何被调用的?

【问题讨论】:

  • 另一种看待它的方式是,为什么不使用管道输出本身的包装脚本呢?因此,Awesome Window Manager 调用 script-wrapper.sh ,其中包含“./script.sh >> awesome.log”
  • 作为 shell 脚本的用户,我更喜欢脚本不要太聪明。如果我想要文件中的输出,我完全有能力自己将其放入文件中。为脚本提供包装器或标志以将输出发送到文件(例如-o filename)将是理想的 - 简单但明确。
  • 好点,但我希望这很简单。有问题的命令是Taskwarrior。我只想能够输入task add blah blah 而不必担心我在哪里输入。如果我每次都需要输入大量重定向或选项,我不会使用它,因为它不够方便。

标签: bash scripting shell redirect


【解决方案1】:

试试这个:

ps -o stat= -p $PPID

如果结果包含“s”(小写字母),则它要么是从命令行运行的,要么是从脚本中后台运行的。区分这两者:

ps -o stat= -p $$

如果没有背景,将包含一个“+”。

这是一张桌子:

Run          $$    $PPID
CL           S+    Ss
CL&          S     Ss+
Script       S+    S+
Script&      S     S
Script(&)    S     Ss
Script&(&)   S     NULL

其中 (&) 表示子脚本是后台运行的,& 表示运行它的父脚本(即“脚本”所指的)是后台运行的。 CL 表示命令行。 NULL 表示 ps 输出 null 且 $PPID 为“1”。

来自man ps

   s    is a session leader
   +    is in the foreground process group

应该注意,这个答案是基于 GNU ps,但是 BSD(包括 OS X)的手册页表明了类似的功能。 GNU ps 是一个混合体,其中包括 BSD 功能等。

【讨论】:

  • 表中的“脚本”是指父脚本,而不是问题主题的脚本。
【解决方案2】:

我相信您真正想知道的是 stdout 是否是终端。如果是,那么您可以(几乎)安全地假设它是一个交互式会话。试试下面的 sn-p:

if [[ -t 1 ]]; then
        echo "Terminal"
else
        echo "Not-a-terminal"
fi

上面的[[ -t 1 ]] 命令用于检查文件描述符 1(即标准输出)是否为终端。

编辑:

请注意,如果您将输出通过管道传输到其他程序,这将指示非终端标准输出。在这种情况下,您可能需要一个更通用的条件来检查标准输入(文件描述符 0):

[[ -t 0 || -t 1 ]]

【讨论】:

  • 但是您可能想知道它是否从命令行运行,即使 stdout 不是终端(例如它在管道中时)。
  • @Dennis:我认为没有通用或神奇的解决方案。 PPID方法是有效的,但是如果通过另一个脚本调用该脚本也会失败。
  • 你说的是第三层吗?脚本调用脚本调用脚本?您仍然可以在我的回答中使用该技术来确定感兴趣的脚本是否是从命令行运行的。
  • @Dennis:是的,看起来是这样......在大多数情况下,检查脚本或其父项的标志中的 + 似乎可以完成这项工作。唯一的例外似乎是调用者脚本被后台处理的情况。也许所有方法的合并将是最通用的?
【解决方案3】:

这是我改编自另一篇关于此主题的帖子的功能。它将所有父进程与$shells 变量中列出的项目进行匹配。当它完成时,$iscli 被设置为 0 或 1。如果它被设置为 0,那么你就知道它是从一个 shell 运行的,或者你认为 shell 足以达到这个目的。如果它设置为 1,那么您知道所涉及的程序未被批准。当我想为每个脚本提供不同的输出时,我将它用于我想在 shell 中运行的脚本和通过 PHP 运行的脚本。

在需要$iscli 具有值之前,您当然需要在没有任何参数的情况下首先调用该函数。

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
}

【讨论】:

    猜你喜欢
    • 2012-05-19
    • 2014-06-05
    • 2015-06-27
    • 2019-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多