如何从脚本中获取最近的交互式祖先 shell 的名称?

如何从脚本中获取最近的交互式祖先 shell 的名称?

我知道可以使用$-contains或不或检查是否$PS1为空来判断 shell 是否是交互式的。

但是,这些解决方案仅适用于当前 shell。

我有一个 bash 脚本试图找到交互式的直接父 shell。

例如:

  1. 交互式 shell:zsh
  2. bash 脚本 1 执行 bash 脚本 2
  3. bash 脚本 2 包含找出即时交互式 shell 的机制

因此,当我们在交互式 shell 下执行 bash 脚本 1 时zsh,我们期望输出为zsh.

当脚本在子 shell 中运行时,我不知道如何做到这一点。

笔记

我希望执行脚本,而不是获取脚本。

阐明

我有一个 bash 脚本试图找到第一个交互式的祖先 shell。

我所说的第一个祖先是指我们在自下而上的进程链扫描过程中遇到的第一个交互式 shell。

例如,在以下情况下: zsh(第一个交互式 shell) -> bash(第二个交互式 shell) -> bash(脚本 1 的批处理 shell) -> bash(脚本 2 的批处理 shell),我们想要输出 bash(第二个交互式 shell)。

答案1

这是一个非常奇怪的要求。您为什么要关心哪个交互式 shell 调用了您的脚本,或者调用了不是交互式 shell 的其他程序,而后者又调用了您的脚本?这有一种非常强烈的气味XY问题

如果你确实需要知道,你可以尝试弄清楚,但我认为没有一种完全可靠的方法,只有一种在典型情况下有效的方法。

从 开始$PPID,跟踪脚本的祖先进程 ( ps -o ppid= -p $ancestor_pid),直到找到您正在寻找的进程,或者找到表明您已经走得太远的进程。

一个简单的策略是在不同的进程组中查找进程 ( ps -o pgid= -p $ancestor_pid)。在正常情况下,如果您的脚本是由交互式 shell(被调用的脚本)调用的,那么您所到达的进程是具有作业控制的 shell,它在单独的进程组中运行您的脚本(的父脚本) 。

以下是此策略可能出现问题的一些示例:

  • 该链上的进程之一已经死亡。
  • 您的脚本不是通过交互式 shell 调用的,而是通过 cron 作业、X11 程序等调用的。

您可能想也可能不想检查该进程的标准输入、标准输出和标准错误(例如,如果您不需要 Linux 之外的可移植性,lsof则使用 或 via )是否与脚本相同的终端。/proc这取决于您想如何处理诸如此类的情况

bash$ xterm -e your_script

答案2

你检查它的选项。

[ "$-" = "${-#*i}" ] ||
echo shell is interactive

您还可以检查其文件描述符。这有点不同。它不一定会告诉您 shell 本身是否是交互式的,但它会告诉您它是否正在与终端通信。

for fd in 0 1 2
do     [ -t "$fd" ] && 
       break 
done|| echo shell fds 0 1 2 are not connected to a terminal.

我们可以稍微破解一下,试图发现我们终端的所有使用过程。

tty_users()
    for fd in 0 1 2 "$@"
    do     [ -t "$fd" ] && {
           fuser "$(tty)"
           break; } <&"$fd"
    done 

它将在当前 shell 中运行,并打印在第一个检测到的终端上运行的进程的进程 ID 列表,与std(in|out|err) (默认情况下 - 传递函数数字参数来测试其他参数)分别。它将 shell var 设置$fd为与该终端关联的文件描述符编号,如果没有任何标准描述符或任何参数与终端关联,则返回 false。

除非您查找终端的会话 ID(如果您有终端),否则上面的内容可能与您所能得到的最接近。

ps -osid= -p"$$"

这将返回给你控制终端所有者的 pid——如果你有的话。

展示:

echo "$$"; sh -c 'sh -c "ps -osid= -p\"\$$\""' 

6023
6023

但你不能依赖这些东西。完全不是。看:

sh -acm 'IFS=\; i=0;eval "$0"'          \
        '[ "$i" -lt 5 ] && eval "$*"'   \
        'ps -opid -opgid -p"$$"'        \
        'sh -acm "$0" "$0" "$@" i=$((i+=1))'
  PID  PGID
28766 28766
  PID  PGID
28768 28768
  PID  PGID
28770 28770
  PID  PGID
28772 28772
  PID  PGID
28774 28774

你看?我运行这些进程的交互式 shell 甚至不在该列表中。每个sh子进程都会启动一个自己的子进程,直到深度达到 5,并且当每个子进程达到深度时,它都会调用ps打印其 PID 和 PGID。每一个进程获得新的 PGID。作业控制与交互式 shell 的关系就像终端一样,只是终端是测试它的更直接的方式。

相关内容