Score:0

Open document in an already-launched Xorg application from bash

tn flag

Is there a way to tell an already running x-program to open a file from bash? (I.e. without invoking a new instance of the program). While I am asking this question in the context of xstata-mp, I am interested more generally if this kind of solution exists in general for Xorg applications.

I have an x-application (xstata-mp, proprietary, which is salient to this question as you will learn) which runs swell. I have managed to make a nice launching script that first checks whether xstata-mp is already running, and if it is, brings it to the foreground, and otherwise launches it. Much like the accepted answer to this question. This is important, since the application is installed as part of a site license allowing only a limited number of concurrent 'seats' (technically I am only supposed to run it once on a single machine).

My problem is that sometimes I want to open a document used by xstata-mp (e.g., a .dta data file, a .do script file, a .sthlp help file, etc.). If I double click on such a file's icon, or select the icon and hit <ENTER> while xstata-mp is already running, the launch script gets called (it is referenced in the exec part of xstata-mp's .desktop file) and xstata-mp is raised to the top of the visible windows, but without opening the document.

For what it is worth, checking with pidof it appears that xstata-mp does not launch a new xstata-mp process if I open multiple documents within it (e.g., using <CTRL>-O); contrast with, say, Firefox and multiple tabs/sites.

Here is the launch script I would like to modify:

# Check if xstata-mp v17 is running
exit_code_pidof_xstata_mp=$(pidof /usr/local/stata17/xstata-mp)

# if xstata-mp v17 IS NOT running, then launch it with argument $1
if [ -z "$exit_code_pidof_xstata_mp" ]
  then
    /usr/local/stata17/xstata-mp -q $1; exit >/dev/null
 # but if xstata-mp v17 IS running, then bring it to front instead
 else
  wmctrl -ia "$(wmctrl -lp | grep "$(pgrep /usr/local/stata17/xstata-mp)" | tail -1 | awk '{ print $1 }')"; exit > /dev/null
  fi
Score:1
in flag

First of all you should find out the correct command to open your document. So try to run the following command and see what you get.

/usr/local/stata17/xstata-mp --help

Of course this is the critical part. And here you rely solely on the functionality of your program. If it does not offer that option, well, then you cannot do it, because all a bash can ever do is start another program. From the 9.4 MB user manual with 399 pages I downloaded from StataCorp LLC, there was not a single hint on how to use xstata-mp.

Maybe the -q option is already what you want. But let's assume the right option to add another document to an already running instance of xstata-mp would be -a (like in VS Code: code -a). Then all you have to do, is to add the following line below your else statement:

/usr/local/stata17/xstata-mp -a "$1"

It would open your document and show the current window now.

Lexible avatar
tn flag
Thanks, mark. +1 I did indeed already read `xstata-mp --help` and also the things not documented there (like the `doedit` argument) in the official documentation. I feared an answer like yours might be in the offing, but posed my question in the hope that there might be some interface standard for Xorg apps along the lines of my request. (If there is, argument `-a` ain't it, by the way. ;). (Also: the `-q` option is mos def irrelevant to my concerns here.)
Lexible avatar
tn flag
Hey mark. I have just learned about, and am playing with `xdotool` which is allows one to manipulate (key strokes, text, pointer input, windows, some related variables, etc.) Xorg programs. When I get my script edited to working, I will compose an answer, but wanted to let know there is a solution for Xorg applications in general! :)))
Lexible avatar
tn flag
Ok, see my answer. :)
Score:0
tn flag

YES: There is a way to tell an already running x-program to open a file from bash!

So, following a tip from StataCorp's technical support folks, it turns out that xdotool (which can be installed with sudo apt install xdotool, if it is not already on your system) offers a solution to exactly the kind of problem in my question. From the project website:

xdotool lets you simulate keyboard input and mouse activity, move and resize windows, etc. It does this using X11’s XTEST extension and other Xlib functions.

⚠️ Note: If you are using Wayland, please be aware this software will not work correctly. ⚠️

With xdotool, you can search for windows and move, resize, hide, and modify window properties like the title. If your window manager supports it, you can use xdotool to switch desktops, move windows between desktops, and change the number of desktops.

To solve my problem with xdotool, I needed to create an xdotool script which I am calling statadoc.xdo:

#!/usr/bin/xdotool
search --name "Stata/MP"
type --window %1 '$1 ' '$2'
key --window %1 Return

A few comments about this short script:

  1. The path may be something other than /usr/bin/xdotool, so be sure to confirm that with which xdotool or similar.
  2. The second line will partially match the name "Stata/MP 17.0" with the string supplied above. This is useful, so that, for example, upgrading the version to 17.1, or 18.x won't break the script. The search command identifies the X-application window I want to interface with.
  3. xdotool scripts accept arguments, following bash-like $1, $2, etc. conventions.
  4. The type command literally types the provided text—in my case, the contents of the two supplied $1 and $2 string arguments in the 1st (and in my case only) window identified by the search command on line 2.
  5. The key command sends an <ENTER> (or <RETURN> if you prefer) to the same Stata window.

Now let's look at my modified launch script, which calls statadoc.xdo in the third to last line (I have added a section in front to recognize whether the supplied argument indicates particular Stata file types—different use commands are needed to gracefully handle each… my example is not complete, but these are the file types I use in the majority of my work):

# Check whether there IS NOT an argument. If not, do nothing.
if [ -z "$1" ]
  then
    break
 # Otherwise, set prefix to default value, and then check 
 # whether file name ends in .hlp, .sthlp, .ado, .do, or .gph
 else
  prefix="use "
  if [ ${1##*.} = "hlp" ] || [ ${1##*.} = "sthlp" ] || [ ${1##*.} = "ado" ] || [ ${1##*.} = "do"  ]
    then
      # If the filename DOES end in one of those four prefixes then
      # change prefix to "doedit" so Stata opens the document with 
      # the do-file editor.
      prefix="doedit "
    fi
  # If the filename ends in .gph, then change prefix to "doedit" so 
  # Stata opens the document with the graph viewer/editor.
  if [ ${1##*.} = "gph" ]
    then
      prefix="graph use "
    fi
  break
  fi

# Check if xstata-mp v17 is running
exit_code_pidof_xstata_mp=$(pidof /usr/local/stata17/xstata-mp)

# if xstata-mp v17 IS NOT running, then launch it with argument $1
if [ -z "$exit_code_pidof_xstata_mp" ]
  then
    /usr/local/stata17/xstata-mp -q $1; exit >/dev/null
 # but if xstata-mp v17 IS running, then bring it to front instead
 else
  # First, call statadoc.xdo with the prefix as the first argument, 
  # and the supplied file path as the second argument 
  /usr/share/stata17/bin/statadoc.xdo "$prefix" " $1"
  wmctrl -ia "$(wmctrl -lp | grep "$(pgrep /usr/local/stata17/xstata-mp)" | tail -1 | awk '{ print $1 }')"; exit > /dev/null
  fi

Note: This is a solution for Gnome running on Xorg.

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.