How to identify the terminal from a script?

Posted on

QUESTION :

And don’t say “$TERM” – it’s always xterm.

How can a bash script tell what terminal it’s running in, specifically whether it’s iTerm, Terminal.app, or actually an xterm?

I ask because reset does not work¹ out of the box on Terminal.app and iTerm2. iTerm2, however, does recognize an escape sequence for doing a terminal reset (x1b]50;ClearScrollbackx07), and if I could detect it, I could override reset with an alias that does the right thing. AFAICT, Terminal.app lacks a reset sequence, and people resort to ridiculous tom-hackery to hack around that.

My end goal here is to have reset work the same whether I’m working on OS X or Linux, working locally or remotely through SSH. (I don’t want to have to try to remember which, and it’s useful to do reset && command-that-outputs-a-bunch and have up-enter work.) Terminal.app and iTerm are throwing a wrench in this plan by not implementing reset correctly.

This means that simply overriding reset isn’t quite it: if I’m on a Linux machine, it needs to know whether I’m using gnome-terminal or iTerm in order to send the right escape sequence.

Is there any way (even if I need an ioctl) to ask the terminal what it really is?

¹For the purposes of this question, reset should clear the screen, reset the cursor, and wipe the scrollback buffer.

ANSWER :

Use $TERM_PROGRAM.

iTerm sets it to iTerm.app, and Terminal.app to Apple_Terminal.

$TERM has nothing at all to do with the terminal emulator currently running, it is just your default terminal and can be set to anything at all. To get the name of the terminal emulator you are running, you can use ps to get the PID of the parent process of your current shell.

NOTE: The following will fail on OSX but should work OK on Linux

The PID of your current shell process is $$. From there, you can use ps to show a process tree, and print the PID of the parent of your current shell session:

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

You can then pass that PID to ps and tell it to print the command name:

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

That will truncate the name, it should be enough for you to figure it out but might not be good for scripting. To get the complete name, you could try

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

This is what I get on my system using a few different terminals:

  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
    

Here’s a portable way to get the name or path of the parent process:

iTerm 2:

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

gnome-terminal in Ubuntu:

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

Terminal.app:

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

Note that if Terminal.app is set to open new shells with the default login shell, the parent process of the shell is login and not the terminal.

The comm column is the full path of the command in OS X and the command name truncated to 15 characters in the procps implementation in Linux.

Leave a Reply

Your email address will not be published. Required fields are marked *