5

I have used pstree to find the name of the parent emulator of running shell script using something similar to the following:

pstree -s $PPID | awk -F '---' '{print $6}'

This works in my current system. I tested in mate-terminal and xterm but not sure if this will work on other Linux systems/platforms and other terminals. Is there a better/tidier (more portable way) way of achieving this?

Vombat
  • 12,884

3 Answers3

10
ps -o comm= -p "$(($(ps -o ppid= -p "$(($(ps -o sid= -p "$$")))")))"

May give you good results. It gives the name of the process that is the parent of the session leader. For processes started within a terminal emulator, that would generally be the process running that terminal emulator (unless things like screen, expect, tmux... are being used (though note that screen and tmux are terminal emulators), or new sessions are started explicitly with setsid, start-stop-daemon...)

Or breaking it down into individual steps using variables (which can also help make the script more self explanatory):

sid=$(ps -o sid= -p "$$")
sid_as_integer=$((sid)) # strips blanks if any
session_leader_parent=$(ps -o ppid= -p "$sid_as_integer")
session_leader_parent_as_integer=$((session_leader_parent))
emulator=$(ps -o comm= -p "$session_leader_parent_as_integer")

The stripping of whitespace around numbers here is done using $((...)) arithmetic expansion. You could also doing it using the split+glob operator (assuming an unmodified $IFS) or as suggested by @ack in comments using xargs:

ps -o sid= -p "$$" |
  xargs ps -o ppid= -p |
  xargs ps -o comm= -p

You could also try parsing wtmp where terminal emulators usually log an entry with their pid associated with the pseudo-terminal device. This works for me on a Debian system provided expect/screen/tmux... are not involved:

ps -o comm= -p "$(
  dump-utmp -r /var/log/wtmp |
  awk -v tty="$(ps -o tty= -p "$$")" -F ' *\\| *' '
    $2 == tty {print $5;exit}')"

(using dump-utmp from GNU acct).

  • 1
    While the first implementation looks kinda Lispy (and don't get me wrong, I like Lisp a lot!) and the second expanded one looks more like Lisp doing Warp 9, Bash isn't such a great Lisp. The other implementations are IMHO much more complicated than they need to be. There is a much simpler, straightforward, down to Earth way of doing it, using just plain old pipes: $ ps -o sid= -p "$$" | xargs ps -o ppid= -p | xargs ps -o comm= -p – ack Jan 23 '22 at 06:15
  • 1
    @ack, very good point, I've added it in. – Stéphane Chazelas Jan 23 '22 at 11:14
2

To find the name of the terminal emulator used by the current shell, you could ask the X window system to give you the name of the window that the shell is currently visible in:

$ xwininfo -id $WINDOWID | awk '/^xwin/ { print $NF }'

This gives back the string "xterm" for me in XTerm, and "urxvt" when I run in Rxvt-unicode. The result will probably be different if you have the habit of changing the window title though, because that's what is being handed back to you here I think.

Kusalananda
  • 333,661
  • What if xwininfo is not available? – Vombat Feb 19 '16 at 13:02
  • Well, what if pstree is not available or if it's incompatible with your original solution? (The -s flag on OpenBSD systems, for example, have different semantics). – Kusalananda Feb 19 '16 at 13:04
  • That is exactly the reason I was looking for something more portable! – Vombat Feb 19 '16 at 13:07
  • 2
    That gives you the window's title which can be overridden at run time or in configuration, you could use eval "$(xprop -notype -id "$WINDOWID" 32i '=$0' _NET_WM_PID)"; ps -o comm= -p "$_NET_WM_PID" to get the process name instead. – Stéphane Chazelas Feb 19 '16 at 13:15
  • 2
    @cofferMug, xwininfo is a standard X command like xprop. If your system has X11 terminal emulators, it should have those commands. – Stéphane Chazelas Feb 19 '16 at 13:16
  • @StéphaneChazelas I run the script on a Gentoo based system and I have xterm, xclock and so on but no xwininfo! Maybe the system is a customized one. – Vombat Feb 19 '16 at 13:34
  • @StéphaneChazelas ps -o comm= ... would cut long process name (gnome-terminal-server => gnome-terminal-). An alternative way, xprop -id $WINDOWID WM_CLASS | cut -d'"' -f2, if instance in WM_CLASS (the first string) was set correctly. – zhazha Jan 17 '18 at 10:52
  • @zhazha, strictly speaking, ps -o comm= does give you the full process name, it's just that the process name on Linux is limited to 15 bytes. – Stéphane Chazelas Jan 17 '18 at 10:56
  • Not all terminal emulators define $WINDOWID – pmav99 Jan 10 '19 at 05:04
  • @pmav99 Sure, and not all scripts run within a terminal emulator either. – Kusalananda Jan 10 '19 at 06:38
1

Building on stephane-chazelas solution to make it work under tmux (i.e. return the terminal emulator that the tmux client is using for display), this seems to work for me:

TERMINAL_EMULATOR="$(ps --pid $(ps --pid $$ -o ppid=) -o comm=)"
if [[ "${TERMINAL_EMULATOR}" =~ tmux ]]; then
    export TERMINAL_EMULATOR=$(ps --pid "$(($(ps --pid $(ps --pid $(tmux display-message -p "#{client_pid}") -o sid=) -o ppid=)))" -o comm=)
else
    export TERMINAL_EMULATOR
fi
Ben
  • 315