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:
- if the user has specified a preference, use it
- else try
xdg-terminal
- else try
x-terminal-emulator
- else try a hard-coded list
Look at standards such as POSIX for portability guarantees. In practice, most POSIX-compliant systems have minor deviations from the specifications, but generally speaking you can rely on the guarantees given in the specification. Most modern unices comply with the specification even if they haven't been formally tested. They may need to be run in a POSIX mode, e.g. setting POSIXLY_CORRECT=1
with bash or making sure that /usr/xpg4/bin
is ahead of /bin
and /usr/bin
in PATH
on Solaris.
Single Unix v2 (an older extension of POSIX) has this to say about link
:
The link()
function will atomically create a new link for the existing file and the link count of the file is incremented by one.
About rename
:
If the link named by the new argument exists, it is removed and old renamed to new. In this case, a link named new will remain visible to other processes throughout the renaming operation and will refer either to the file referred to by new or old before the operation began.
POSIX explicitly states that if the destination exists, its replacement must be atomic. It does not however state that the renaming itself must be atomic, i.e. that there is no point in time when both old and new refer to the file in question, or when neither does. In practice, those properties are true on unix systems, at least with local filesystems.
Furthermore the order of operations is guaranteed: in C, ;
guarantees sequential execution; in sh, ;
/newline guarantees sequential execution (as do &&
and so on); other programming languages offer similar guarantees. So in
unlink("/tmp/foo");
unlink("/tmp/bar");
it is guaranteed that there is no point in time when /tmp/foo
exists but not /tmp/bar
(assuming that /tmp/bar
exists initially). Therefore a concurrent process executing link("/tmp/foo", "/tmp/bar")
cannot succeed.
Note that atomicity does not guarantee resilience. Atomicity is about observable behavior on a live system. Resilience, in the context of filesystems, is about what happens in case of a system crash. Many filesystems sacrifice resilience for performance, so if the execution of unlink("foo"); unlink("bar");
is interrupted (with the current directory on on-disk storage), it is possible that bar
will be deleted and foo
will remain behind.
Some network filesystems give fewer guarantees when operations happen on different clients. Older implementations of NFS were notorious for this. I think modern implementations are better but I have no experience of modern NFS.
Best Answer
I don't think you can.
xterm
need not be installed everywhere, and indeed probably isn't by default. Especially when a desktop environment is in use that provides its own terminal.I think your best bet is probably to check for the existence of a few different terminals (say,
xdg-terminal
,x-terminal-emulator
,gnome-terminal
,konsole
,xterm
). And maybe work towards getting xdg-terminal actually added to the FreeDesktop.org standards.Then you'll get to find that different terminals have different ways to run commands, and sometimes even different versions of the same terminal... e.g., see Debian Bug #648271.
You should probably also give the admin/user a way to set a custom command. It'll surely be needed.