Shell Script – Launch Terminal Emulator Without Knowing Installed Ones

portabilityshell-scriptterminal-emulator

I need to write a script that will launch a terminal emulator and run a particular command in it. Since I don't know which terminal emulators have been installed on the target system, how could I find which terminal emulators have been installed?

I've looked at this thread. However, I'm not really very inclined to make a hardcoded check, and I'm searching for a generic solution.

I've come up this very dirty hack, but I'm really not sure how this would fare:

termdetect:

#!/bin/bash

IFS=:
for i in $PATH; do
  find "$i" -type f -exec isterminal {} \;
done

isterminal:

#!/bin/bash

if [[ ! -z "$1" ]]; then
  if [[ "$(head -c 4 "$1")" == $'\x7fELF' ]]; then
    if ! grep editor "$1" > /dev/null; then
      if grep -E '(vte_terminal_|libutempter|rxvt_)' "$1" > /dev/null; then
        echo "$1"
      fi
    fi
  fi
fi

Is there an easy generic solution that I'm perhaps missing? Or do I have no other way than to scour binaries for hints or use a hardcoded list?

Best Answer

Your heuristics are pretty bad. Here's what they find on a Debian wheezy machine of mine (I've manually marked correct hits with a +).

/usr/bin/gcm-calibrate
/usr/bin/gnome-terminal        +
/usr/bin/mosh-client   
/usr/bin/mosh-server
/usr/bin/mrxvt                 +
/usr/bin/mrxvt-full            +
/usr/bin/roxterm               +
/usr/bin/rxvt-unicode          +
/usr/bin/urxvt                 +
/usr/bin/urxvtd
/usr/bin/vinagre
/usr/bin/x-terminal-emulator   +
/usr/bin/xfce4-terminal        +
/usr/bin/xterm                 +

If you pick the first alphabetic match, it's a color calibrator.

Here's the list of alternatives for x-terminal-emulator, which is the correct answer (as far as distribution-managed programs is concerned) on this Debian machine:

/usr/bin/Eterm
/usr/bin/gnome-terminal.wrapper
/usr/bin/koi8rxterm
/usr/bin/konsole
/usr/bin/lxterm
/usr/bin/mlterm
/usr/bin/mrxvt-full
/usr/bin/roxterm
/usr/bin/rxvt-xpm
/usr/bin/rxvt-xterm
/usr/bin/urxvt
/usr/bin/uxterm
/usr/bin/xfce4-terminal.wrapper
/usr/bin/xterm
/usr/bin/xvt

You're finding 7 out of 15 (accounting for vagaries of names), which isn't a very good hit rate.

You could try to refine your heuristics, but it's a never-ending game. The minimum would be to retain only executables that both link against some X11 libraries and call some master terminal function or utility like getpt or grantpt, but that's bound to find a lot of spurious positives too, and to miss executables where the call is in a library (like konsole). And this will miss native code executables that are accessed through a shell wrapper (there are several in my list).

Detecting whether a program is an X terminal emulator from the executable content is hopeless.

Detecting programs by name is a far better heuristic.

Note also that different programs have a different command line syntax. Most of them accept -T TITLE and -e COMMAND, but they have different parsing rules for -e (going through a shell or not, taking multiple arguments or not) so you should pass a single parameter after -e, not containing any shell special characters.

On Debian and derivatives, x-terminal-emulator is guaranteed to be an X terminal emulator that supports -T and -e, such that -e uses all subsequent command line parameters as a command and its argument and doesn't do shell expansion on them. Because some emulators behave differently, x-terminal-emulator is sometimes a wrapper that takes a more xterm-like syntax (gnome-terminal.wrapper and xfce4-terminal.wrapper are two examples of this).

I don't know of anything like this on other unix variants. For anything like portability, a hard-coded list of program names is your best bet.

Furthermore, detecting installed terminal emulators won't give you any idea about the user's preferences. For example, if I use Gnome, I probably don't want your application to launch Konsole — unless I happen to prefer Konsole, but how would you know that?

The de facto standard for user preferences is via FreeDesktop.org specifications. Freedesktop publishes an xdg-utils package which contains programs such as xdg-open to open a document in the proper application, xdg-email to launch the user's preferred email composing application, and more to the point xdg-terminal to open the user's preferred terminal emulator.

This would make xdg-terminal the right thing to use, and you should use it if it's there. Unfortunately, xdg-terminal isn't available everywhere; for example, Debian doesn't ship it (it's a long-idle bug). Furthermore xdg-terminal allows a single command argument, and is not consistent about what kind of expansion this argument undergoes.

Whatever you do, make sure to provide an easy way for the caller to specify a preference. Even xdg-terminal doesn't always get it right.

Thus your best strategy is:

  1. if the user has specified a preference, use it
  2. else try xdg-terminal
  3. else try x-terminal-emulator
  4. else try a hard-coded list
Related Question