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
+
).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: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
orgrantpt
, but that's bound to find a lot of spurious positives too, and to miss executables where the call is in a library (likekonsole
). 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
andxfce4-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 asxdg-open
to open a document in the proper application,xdg-email
to launch the user's preferred email composing application, and more to the pointxdg-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). Furthermorexdg-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:
xdg-terminal
x-terminal-emulator