【问题标题】:using sed and pstree to display the type of terminal being used使用 sed 和 pstree 显示正在使用的终端类型
【发布时间】:2013-08-26 21:14:50
【问题描述】:

我一直在尝试仅显示用作名称的终端类型。例如,如果我使用的是 konsole,它将显示 konsole。到目前为止,我一直在使用这个命令。

pstree -A -s $$

输出这个。

systemd---konsole---bash---pstree

我有以下可以从该行提取 konsole

pstree -A -s $$ | sed 's/systemd---//g;s/---.*//g' | head -1

并且正确输出 konsole。但是有些人只使用 pstree 命令的输出可能看起来像这样。

systemd---kdeinit4---terminator---bash---pstree

或者这个

systemd---kdeinit4---lxterminal---bash---pstree

然后当我添加 sed 命令时,它会提取 kdeinit4 而不是终止符。我可以想到几个场景来提取终端类型,但没有一个场景不包含条件语句来检查特定类型的终端。我遇到的问题是我无法准确预测终端名称前面或后面可能有多少非或非相关事物或它们将是什么,我也无法准确预测终端名称将是什么。有没有人对此有任何想法?

【问题讨论】:

  • @JoSo 我在$TERM 中有xterm,但我正在使用terminator
  • 我也想要这个结果。今晚我做了更多的观察,下面是 Ubuntu 下的行,例如将 init - mdm 作为祖先。现在我认为我可以在 .profile 中 export 一些环境变量,如 START_TERM ,这可以为我提供任何级别的脚本甚至像 fork() 或 @ 这样的 desired 结果987654334@。 (到目前为止一切顺利......)

标签: linux bash shell sed


【解决方案1】:

你可以使用

ps -p "$PPID" -o comm=

或者

ps -p "$PPID" -o fname=

如果您的 shell 没有设置 PPID 变量,您可以使用它来获取它

ps -p "$(ps -p "$$" -o ppid= | sed 's|\s\+||')" -o fname=

另一个理论是,当前 shell 的父进程与 shell 不属于同一个 tty,实际上可能是产生虚拟终端的那个,所以我们也可以这样找到它:

#!/bin/bash

shopt -s extglob

SHELLTTY=$(exec ps -p "$$" -o tty=)
P=$$

while read P < <(exec ps -p "$P" -o ppid=) && [[ $P == +([[:digit:]]) ]]; do
    if read T < <(exec ps -p "$P" -o tty=) && [[ $T != "$SHELLTTY" ]]; then
        ps -p "$P" -o comm=
        break
    fi
done

【讨论】:

  • 只有从终端的直接子节点运行时才有效。 (例如,尝试bash -c 'ps -p $PPID -o comm='。所以它并没有真正解决“我遇到的问题是我无法准确预测终端名称后面可能有多少非或非相关的东西”
  • 是的,我预料到了。有一个解决方案,但前提是 OP 有这个要求。我实际上想先提供一个更简单的解决方案。
  • 我评论中的引用来自 OP,所以我想 OP 确实如此
  • 哦,我没有注意到前面的事情。但他给出的示例问题只是关于终端后面的任何事情。
  • 不错。我本可以使用$(ps -p $$ -o tty=);它比依赖 dev 0 作为符号链接更通用。做事总是有几种方法。
【解决方案2】:

我不知道如何在您的系统上隔离终端名称,但作为解析练习,假设终端直接运行您的 bash,您可以通过管道传输 pstree 输出:

 awk -F"---bash---" ' NF == 2 { count = split( $1, arr, "---" ); print arr [count]; }'

这将在您的示例中找到“---bash---”之前的单词

konsole
terminator
lxterminal

如果您想要不同的外壳类型,您可以扩展字段分隔符以包含它们,例如:

awk -F"---(bash|csh)---" ' NF == 2 { count = split( $1, arr, "---" ); print arr[count]; }'

考虑一条假想的线,例如:

systemd---imaginary---monkey---csh---pstree

awk 会发现“monkey”作为终端名称以及您的测试集中的任何名称。

【讨论】:

    【解决方案3】:

    这里没有保证,但我认为这在 linux 上大部分时间都可以工作:

    ps -ocomm= $(lsof -tl /proc/$$/fd/0 | grep -Fxf <(lsof -t /dev/ptmx))
    

    可能需要稍微解释一下,但请参阅man psman lsof 和(尤其是)man pts 了解信息。

    /dev/ptmx 是一个伪 tty 主机(在现代 linux 系统和其他一些 unix(-like) 系统上)。如果程序是终端仿真器、telnet/ssh 守护程序或其他需要强制终端的程序(例如screen),则程序将打开其中一个。模拟器将它想要“键入”的内容写入伪 tty 主机,并从伪 tty 从机读取结果。

    /proc/$$/fd/0 是进程$$ 的标准输入(即执行命令的shell)。如果 stdin 没有被重定向,这将是一个符号链接到一些从属伪对象,/dev/pts/#。那是 /dev/ptmx 设备的另一端,因此上面列出的所有打开 /dev/ptmx 的程序也将打开一些 /dev/pts/# 从属设备。 (您可能认为您可以使用/dev/stdin/dev/fd/0 而不是/proc/$$/fd/0,但这些将由lsof 本身打开,因此将是它的标准输入;由于lsof 的实现方式,即将不起作用。)lsof-l 选项使其遵循符号链接,因此将导致它显示与当前 shell 具有相同 pts 打开的进程。

    lsof-t 选项使其产生“简洁”输出,仅包含 pid,每行一个。 -Fxgrep 的选项使其匹配字符串,而不是正则表达式,并强制进行整行匹配; -f FILE 选项使其接受来自FILE 的字符串(在这种情况下是进程替换),每行一个。

    最后,ps -ocomm= 打印出与 pid 对应的“命令”(默认为 8 个字符)。

    简而言之,该命令查找具有主伪 tty 的终端仿真器和其他主类似程序的列表,以及使用伪 tty 从属的进程列表;找到两者之间的交集,然后查找命令名称以获取任何结果。

    【讨论】:

      【解决方案4】:
      curTerm=$(update-alternatives --query x-terminal-emulator | grep '^Best:')
      curTerm=${curTerm##*/}
      
      printf "%s\n" "$curTerm"
      

      结果是

      terminator
      

      当然可以不一样。
      现在您可以在 sed 命令中使用 $curTerm 变量。

      但我不确定这是否能与符号链接一起正常工作。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-09-18
        • 2013-09-21
        • 1970-01-01
        • 2023-03-20
        • 2019-04-22
        • 1970-01-01
        • 1970-01-01
        • 2022-01-06
        相关资源
        最近更新 更多