5

If you type this simple test command:

gnome-terminal -x bash -c "ls;sleep 3"

You will find that it returns immediately (the newly created terminal, of course, lingers for three seconds). This is in contrast to, say, rxvt (same command but with e).

If you want a blocking start, the historical consensus seems to have been to use --disable-factory. Unfortunately, this does not work anymore (tested 3.14.2).

So, how do I start the terminal in a non-asynchronous manner?

Bonus: konsole, lxterminal, and xfce4-terminal at least also have the same problem. Commands for those?

14
  • 1
    What exactly you're trying to achieve ? Start terminal , let the programs finish, and then use the shell ? May 22, 2015 at 15:15
  • The answer you linked is also from 2012, I'd say it's a little dated May 22, 2015 at 15:16
  • 1
    Is non-asynchronous = synchronous?
    – A.B.
    May 22, 2015 at 15:16
  • So you want that the command you use opens a new terminal window, executes the ls command, waits 3 seconds, then closes the terminal window and returns control to the first terminal window now, so that you can't continue in the first window, while the second one is open. Correct?
    – Byte Commander
    May 22, 2015 at 15:29
  • 1
    @Helio As mentioned in the question, --disable-factory does not work any more in the current version, although it is still listed in the manpage. Sorry.
    – Byte Commander
    May 22, 2015 at 19:18

5 Answers 5

4

I use a method that has some similarities to the answer of terdon (and was created with some help of him - Thank you @terdon for this!), but has a slightly different approach:

I create a temp file so that the child terminal can communicate with the parent terminal and tell it the PID of its corresponding bash instance. Then I let the parent terminal read the temp file and remember the PID, delete the file and continue by checking every 100ms (delay can be changed) whether the child terminal's bash instance is still running. If not, the terminal was closed either manually or because the command finished. Then the launching command finishes and the parent terminal is usable again.

Advantage of this approach:
All commands that will be launched (ls; sleep 3 or any replacement for those) are executed after the command that is responsible to allow detection of the terminal closing. So if the inner command hangs or you close the terminal manually before it ends, the mechanism is still working and will continue execution of the outer script instead of running into infinite loops.


The code as one-liner with debug output ("launched" after the child terminal window was opened, "terminated" after it was closed) and accuracy of 0.1 seconds is:

pidfile=$(mktemp); gnome-terminal -x bash -c "echo \$$>$pidfile; ls; sleep 3"; until [ -s $pidfile ]; do sleep 0.1; done; terminalpid=$(cat "$pidfile"); rm $pidfile; echo "launched"; while ps -p $terminalpid > /dev/null 2>&1; do sleep 0.1; done; echo "terminated"

Or without debug output:

pidfile=$(mktemp); gnome-terminal -x bash -c "echo \$$>$pidfile; ls; sleep 3"; until [ -s $pidfile ]; do sleep 0.1; done; terminalpid=$(cat "$pidfile"); rm $pidfile; while ps -p $terminalpid > /dev/null 2>&1; do sleep 0.1; done

Almost the same code but with more flexibility written as bash script:

#! /bin/bash

delay=0.1
pidfile=$(mktemp)

gnome-terminal -x bash -c "echo \$$>$pidfile; ls; sleep 3"

until [ -s $pidfile ] 
    do sleep $delay
done
terminalpid=$(cat "$pidfile")
rm $pidfile
echo "launched"
while ps -p $terminalpid > /dev/null 2>&1
    do sleep $delay
done
echo "terminated"

You may omit the echo "launched" and echo "terminated" lines of course, as well as you can change the delay=0.1 line to another delay between two checks of the terminal state (in seconds) if you need it to be more or less accurate.

To execute another custom command in the child terminal, replace the line

gnome-terminal -x bash -c "echo \$$>$pidfile; ls; sleep 3"

with the following (insert your command instead of the capitalized placeholder!)

gnome-terminal -x bash -c "echo \$$>$pidfile; INSERTYOURCOMMANDSHERE"
6
  • I like that this is safe(r) than @terdon's current answer, which is similar. I don't suppose Bash has a thread yield one could use instead of sleep?
    – geometrian
    May 22, 2015 at 19:51
  • Not that I knew one... But as it supports floating point arguments so that you can set short delays like 0.1s, this is no big problem, I would say.
    – Byte Commander
    May 22, 2015 at 19:53
  • I agree, but if such a thing exists, it would have ~same latency as the scheduler (probably less than whatever delay you might reasonably put) and use less CPU than busy looping like in terdon's answer.
    – geometrian
    May 22, 2015 at 19:57
  • I've forked it into its own question.
    – geometrian
    May 22, 2015 at 20:09
  • So as you now have a set of working answers (and forked the subquestion about yielding to SO), you should consider accepting / voting on them...
    – Byte Commander
    May 22, 2015 at 20:20
3

In their infinite wisdom, the GNOME devs decided to remove that option. Unfortunately, their wisdom did not extend to also updating their man page which still lists it. So, it looks like gnome-terminal will always be run int the background and the parent shell session will be returned to immediately. To get around this, you have a few options:

  1. Just use another terminal. I tried with xterm, rxvt and GNOME terminator, all of which worked as expected.

  2. Use some ugly hacks. gnome-terminal, when first run, launches /usr/lib/gnome-terminal/gnome-terminal-server. For some reason, this means that the process is finished as soon as you launched it. To illustrate:

    $ gnome-terminal  -x sh -c "ls;sleep 30" 
    [1] 5896
    $ jobs
    [1]+  Done                    gnome-terminal -x sh -c "ls;sleep 30"
    

    As you can see above, after launching it in the background, the launched job exits immediately. This means that my first thought of launching it in the background and then using $! to check whether it is still running won't work. This means you'll have to do something less elegant like creating a file:

    tmpfile=$(mktemp); gnome-terminal  -x sh -c "ls;sleep 30; rm $tmpfile" 
    while [ -e $tmpfile ] ; do :; done
    

    The commands above will i) create a temporary file (tmpfile=$(mktemp)); ii) launch gnome-terminal, telling it to delete $tmpfile when finished and iii) do nothing (:) as long as the temp file exists while [ -e $tmpfile ]. This will result in a terminal that waits until the process run by gnome-terminal has finished before continuing.

3
  • 1
    Your command is good and short, but it has one big disadvantage! If I close the child terminal window or otherwise terminate it so that your rm $tmpfile is not reached, the whole script will run into an infinite loop and needs to be canceled with Ctrl-C.
    – Byte Commander
    May 22, 2015 at 18:45
  • Re: 1, indeed, but my application is providing compatibility, not demanding it (my application actually also explicitly supports xterm, rxvt, and terminator, among others, already). Re: 2, +1 this is a neat idea, and it forms the basis of the accepted answer. Though simpler, I think waiting on the process ID is a better and safe(r) idea. Thanks!
    – geometrian
    May 23, 2015 at 2:35
  • @imallett yes, this approach could fail if you force close the terminal before the process is finished. What I don't understand is why in the world you need a terminal in the first place? Why not just run the commands you need to run by themselves? Why do you need to open 2 separate terminals? I get the feeling this might be an XY problem.
    – terdon
    May 23, 2015 at 11:54
2

The Ubuntu maintainers of the gnome-terminal package noticed this issue and created a wrapper script (in Ubuntu package gnome-terminal-3.14.2-0ubuntu3) to re-enable the --disable-factory option; however, the wrapper script doesn't work!

From the changelog http://changelogs.ubuntu.com/changelogs/pool/main/g/gnome-terminal/gnome-terminal_3.14.2-0ubuntu3/changelog:

gnome-terminal (3.14.2-0ubuntu3) vivid; urgency=medium

  • debian/gnome-terminal: Add a wrapper script to launch gnome-terminal with a different app-id when a user passes the now ignored --disable-factory option. This should restore compatibility with old launchers for users who are upgrading. [...]

I can't navigate the Ubuntu "Launchpad" (so much for open source) but the wrapper script can be found in https://launchpad.net/ubuntu/+archive/primary/+files/gnome-terminal_3.14.2-0ubuntu3.debian.tar.xz (called gnome-terminal.wrap).

The bug is that the gnome-terminal.wrap script is waiting on the wrong child process; it should be waiting on the terminal server, not the terminal client. The fix is to change the two methods server_appeared and spawn_terminal_server as follows:

    def server_appeared(self, con, name, owner):
        # start gnome-terminal now
        gt = Gio.Subprocess.new(['/usr/bin/gnome-terminal.real',
                                 '--app-id', name] +
                                self.args,
                                Gio.SubprocessFlags.NONE)
        # removed a line here: gt.wait_async(...)

    def spawn_terminal_server(self, name):
        ts = Gio.Subprocess.new(['/usr/lib/gnome-terminal/gnome-terminal-server',
                                 '--app-id',
                                 name],
                                Gio.SubprocessFlags.NONE)
        ts.wait_async(None, self.exit_loop, ts)

You can download the fixed file from: https://gist.github.com/ecatmur/00893506a23e828c6688.

I've notified the package maintainer so hopefully it should be fixed fairly soon.


Another interesting fact: gnome-terminal can be built with an alternative client called gterminal which has a --wait option which seems to do exactly what you want. However, unfortunately Ubuntu don't build or install it in their gnome-terminal package.

1
  • +1 for finding the bug, suggesting the fix, and pushing it upstream. Unfortunately, my application needs to work with vanilla builds and be backward compatible (and in any case it doesn't solve the problem for lxterminal &tc., which I was unofficially also asking about).
    – geometrian
    May 22, 2015 at 19:49
0

I've been using newer gnome-terminal, and the behavior you've described for gnome-terminal appears to be the same for konsole, lxterm, and rxvt (tried all 3 ). So since OP has not answered any comments so far to clarify what he or she wants , I'm making assumption that OP wants to continue using the parent terminal without waiting for child terminal to finish.

That can be achieved with gnome-terminal &. If you want to avoid child terminal being closed when parent exits, use nohup gnome-terminal &. To avoid seeing error output in parent terminal use gnome-terminal 2> /dev/null & or nohup gnome-terminal 2> /dev/null & .

4
  • Thanks for the answer, but I actually want precisely the opposite. On my system, gnome-terminal is implemented as a Python script that opens the terminal asynchronously already. xterm might be basic enough to reproduce the behavior I want.
    – geometrian
    May 22, 2015 at 16:25
  • So what you want is for parent terminal to wait for child terminal finish job first ? May 22, 2015 at 16:34
  • I'd say run ( gnome-terminal -x bash -c "ls; sleep 3" && echo done ) or something like that May 22, 2015 at 16:37
  • Yes. Unfortunately that command doesn't have the desired effect since gnome-terminal starts a terminal asynchronously, returning immediately.
    – geometrian
    May 22, 2015 at 19:23
0

For people in Feb. 2017 < t < March 2018, who came to this site via google, the simple solution is:

gnome-terminal --disable-factory -e "cmd"

works and starts gnome-terminal in a synchronous/blocking manner as expected.

Tested under:

  • Ubuntu 16.04
  • gnome-terminal 3.18.3
1
  • 3.22.1: Failed to parse arguments: Option "--disable-factory" is no longer supported in this version of gnome-terminal
    – stevesliva
    Mar 19, 2018 at 18:29

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .