如何从脚本中识别终端?


8

而且不要说“ $TERM”-总是这样xterm

bash脚本如何分辨运行在哪个终端,特别是iTerm,Terminal.app或实际上是xterm?

我问,因为reset在Terminal.app和iTerm2上开箱即用不起作用¹。但是,iTerm2确实可以识别出进行终端复位(\x1b]50;ClearScrollback\x07)的转义序列,并且如果可以检测到它,则可以reset使用做正确的事情的别名进行覆盖。AFAICT,Terminal.app缺少重设序列,人们诉诸于荒谬的tom-hackery来破解它

我的最终目标是reset无论在OS X还是Linux上都可以进行相同的工作,无论是在本地还是通过SSH进行远程工作。(我不想试图记住哪一个,这样做reset && command-that-outputs-a-bunch并进行上一步工作很有用。)Terminal.app和iTerm通过未reset正确实施在该计划中投入了精力。

这意味着仅仅重写reset还不够:如果我在Linux机器上,它需要知道我使用的是gnome-terminaliTerm还是iTerm,以便发送正确的转义序列。

有什么办法(即使我需要ioctl)也可以询问终端机的实际情况?

¹就此问题而言,重设应清除屏幕,重设光标并擦拭回滚缓冲区。


好吧,这是一个功能,而不是错误,或者说。如果打开Terminal.app的设置,终端选项卡并将回滚行设置为0,会发生什么情况?这会禁用当前行上方的所有文字,还是仅禁用屏幕上方上方的所有文字?我知道,这不完全是您要求的,值得一试。
nitro2k01 2013年

@ nitro2k01:我不确定会完成什么?我回滚。我只希望能够不时地清除它,最好使用清除它reset,因为这是我的手指知道的。
Thanatos

至于您的“重置”问题:澄清一下,您期望该reset命令清除终端仿真器的回滚内容,但这不能保证做到这一点,因为回滚是终端仿真器特定的功能,而不是真正的终端的一部分。但是,终端支持ED(显示擦除)转义序列的扩展,以消除向后滚动ESC [ 3 J。您可以清除屏幕,然后使用它,例如reset && printf '\e[3J’
Chris Page

请参阅我在这里的答案apple.stackexchange.com/a/113168/6883,以了解有关“擦除显示中”扩展程序的更多详细信息。
克里斯·佩奇

@ChrisPage:我意识到终端是古怪的东西,但是如果您声明自己是xterm(通过TERM=xterm),我希望您模拟XTerm的超集,该超集将在重置时清除其回滚。(就像您将转义序列发送为“ blue”一样,您可能希望得到blue。)当然,我的xterm告诉我Erase is backspace.,我很庆幸没有做任何其他事情。那很烦人。
Thanatos 2014年

Answers:


10

使用$TERM_PROGRAM

iTerm将其设置为iTerm.app,Terminal.app设置为Apple_Terminal


嘿,好吧,这显然比我的技巧+1 :)好。hack应该适用于任何终端仿真器,而不仅仅是这两个。
terdon

没有检测每种终端仿真器的单一方法。例如,对于xterm,您可以检查$ XTERM_VERSION变量。什么可能是在OS X上三个最流行的仿真器,将照顾
克里斯·佩奇

接受这一点,因为这是一个好的开始。不幸的是,这并不是一件容易的事,因为该变量在SSH会话中不可用。我怀疑有些配置可以编辑以转发它。
Thanatos

1
@Thanatos sshd_configAcceptEnv。由于此信息(您的Shell与运行终端的外壳在不同的主机上运行)是非常重要的信息,因此它确实应该成为问题的一部分。
丹尼尔·贝克

@DanielBeck:并非完全如此;我想在本地和远程执行相同的操作。我要寻找的是,无论我是在OS X还是Linux上,在本地还是远程工作,在终端中键入“ reset”都会导致终端复位。我已经将此编辑为问题。
Thanatos 2014年

1

$TERM与当前运行的终端仿真器完全无关,它只是您的默认终端,可以设置为任何终端。要获取正在运行的终端仿真器的名称,可以使用它ps来获取当前shell的父进程的PID。

注意:以下操作在OSX上将失败,但在Linux上应该可以正常工作

您当前的shell进程的PID是$$。从那里,您可以ps用来显示进程树,并打印当前shell会话的父进程的PID:

ps -axjf | awk -v pid=$$ '($2==pid){print $1}'

然后,您可以将该PID传递给ps并告诉它打印命令名称:

ps -o comm=  $(ps -axjf | awk -v pid=$$ '($2==pid){print $1}')

这将截断该名称,足以让您弄清楚它的名称,但可能不适合编写脚本。要获取全名,您可以尝试

ps --no-headers $(ps -axjf | awk -v pid=$$ '($2==pid){print $1}') | awk '{print $NF}'

这是我使用几个不同终端在系统上得到的:

  1. terminator

    $ ps --no-headers $(ps -axjf | awk -v pid=$$ '($2==pid){print $1}') | 
       awk '{print $NF}'
    /usr/bin/x-terminal-emulator
    
  2. gnome-terminal

    $ ps --no-headers $(ps -axjf | awk -v pid=$$ '($2==pid){print $1}') | 
       awk '{print $NF}'
    /usr/lib/gnome-terminal/gnome-terminal-server
    
  3. xterm

    $ ps --no-headers $(ps axjf | awk -v pid=$$ '($2==pid){print $1}') | 
       awk '{print $NF}'
    xterm
    

在OS X上不起作用:ps: illegal option -- f
Daniel Beck

@DanielBeck真是BSD风格,谢谢。您能否再试一次更新的版本?-根据OSX的,在选项之前添加a 应该可以man ps
terdon

更好,但仍然无法正常工作。输出格式不同(PID为$2,PPID为$3),并且外壳程序的父进程可能是login(取决于Terminal config),具有不同的参数。然而,它的父进程Terminal--no-headers不存在,则需要类似的东西tail -n1。而且由于所有带有UI的进程都有一个参数-psn_0_...awk '{print $NF}'所以也不起作用。
丹尼尔·贝克

@DanielBeck很好。好的,谢谢,我将明确指出这在OSX上失败。
terdon

请注意,这仅在本地有效。您不能使用这种方法来确定远程客户端上正在使用哪种终端仿真器。最好的答案是查找变量$ TERM_PROGRAM(如@DanielBeck的答案所述)和$ XTERM_VERSION,以推导仿真器应用程序。
克里斯·佩奇

0

这是获取父进程名称或路径的可移植方法:

iTerm 2:

$ ps -p $(ps -p $$ -o ppid=) -o comm=
/Applications/iTerm.app/Contents/MacOS/iTerm

Ubuntu中的gnome-terminal:

$ ps -p $(ps -p $$ -o ppid=) -o comm=
gnome-terminal

Terminal.app:

$ ps -p $(ps -p $$ -o ppid=) -o comm=
login

请注意,如果将Terminal.app设置为使用默认的登录外壳程序打开新外壳程序,则外壳程序的父进程login不是终端程序。

comm列是OS X中命令的完整路径,在Linux中的procps实现中命令名被截断为15个字符。


在iTerm中尝试过并得到了login-我是否有一段时间自定义我的配置文件以运行命令login -fp danielbeck
丹尼尔·贝克
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.