Блокировка запуска терминала

Если вы наберете эту простую тестовую команду:

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

Вы обнаружите, что он возвращается немедленно (вновь созданный терминал, конечно, задерживается на три секунды). Это в отличие от, скажем, rxvt (та же команда, но с e).

Если вы хотите блокировать начало, исторический консенсус, кажется, должен был использовать--disable-factory, К сожалению, это больше не работает (протестировано 3.14.2).

Итак, как мне запустить терминал не асинхронно?

Бонус: konsole, lxterminal, а также xfce4-terminal по крайней мере, тоже есть такая же проблема. Команды для тех?

5 ответов

Решение

Я использую метод, который имеет некоторые сходства с ответом terdon (и был создан с некоторой помощью его - спасибо @terdon за это!), Но имеет немного другой подход:

Я создаю временный файл, чтобы дочерний терминал мог связаться с родительским терминалом и сообщить ему PID соответствующего экземпляра bash. Затем я позволил родительскому терминалу прочитать временный файл и запомнить PID, удалить файл и продолжить, проверяя каждые 100 мс (задержка может быть изменена), работает ли экземпляр bash дочернего терминала. Если нет, терминал был закрыт либо вручную, либо потому, что команда завершилась. Затем команда запуска завершается, и родительский терминал снова может использоваться.

Преимущество такого подхода:
Все команды, которые будут запущены (ls; sleep 3 или любая их замена выполняются после команды, которая отвечает за обнаружение закрытия терминала. Таким образом, если внутренняя команда зависает или вы закрываете терминал вручную до его завершения, механизм все еще работает и продолжит выполнение внешнего скрипта вместо выполнения бесконечных циклов.


Код как однострочный с выводом отладки ("запускается" после открытия дочернего окна терминала, "прекращается" после его закрытия) и точностью 0,1 секунды:

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"

Или без отладочного вывода:

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

Почти такой же код, но с большей гибкостью, написанный как bash-скрипт:

#! /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"

Вы можете опустить echo "launched" а также echo "terminated" линии, конечно, а также вы можете изменить delay=0.1 линия к другой задержке между двумя проверками состояния терминала (в секундах), если вам нужно, чтобы он был более или менее точным.

Чтобы выполнить другую пользовательскую команду в дочернем терминале, замените строку

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

со следующим (вставьте вашу команду вместо заглавного заполнителя!)

gnome-terminal -x bash -c "echo \$$>$pidfile; INSERTYOURCOMMANDSHERE"

В своей бесконечной мудрости разработчики GNOME решили убрать эту опцию. К сожалению, их мудрость не распространяется также на обновление их man страница, которая все еще перечисляет это. Итак, похоже gnome-terminal всегда будет выполняться в фоновом режиме, и сеанс родительской оболочки будет немедленно возвращен. Чтобы обойти это, у вас есть несколько вариантов:

  1. Просто используйте другой терминал. Я пробовал с xterm, rxvt и GNOME terminator, все они работали как положено.

  2. Используйте некоторые уродливые хаки. gnome-terminalпри первом запуске запускает /usr/lib/gnome-terminal/gnome-terminal-server, По какой-то причине это означает, что процесс завершается, как только вы его запустили. Проиллюстрировать:

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

    Как вы можете видеть выше, после запуска в фоновом режиме запущенное задание немедленно завершается. Это означает, что моя первая мысль запустить его в фоновом режиме, а затем использовать $! проверить, работает ли он по-прежнему, не сработает. Это означает, что вам придется сделать что-то менее элегантное, например, создать файл:

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

    Команды выше: i) создаст временный файл (tmpfile=$(mktemp)); ii) запустить gnome-терминал, сказав, чтобы он удалил $tmpfile когда закончите и iii) ничего не делать (:) пока существует временный файл while [ -e $tmpfile ], Это приведет к тому, что терминал будет ждать, пока процесс не будет запущен gnome-terminal закончил, прежде чем продолжить.

Сопровождающие Ubuntu пакета gnome-terminal заметили эту проблему и создали скрипт-обертку (в пакете Ubuntu gnome-terminal-3.14.2-0ubuntu3) чтобы снова включить --disable-factory вариант; однако скрипт-обёртка не работает!

Из журнала изменений http://changelogs.ubuntu.com/changelogs/pool/main/g/gnome-terminal/gnome-terminal_3.14.2-0ubuntu3/changelog:

гном-концевой (3.14.2-0ubuntu3) яркий; Актуальность = средняя

  • debian / gnome-терминал: добавьте скрипт-обертку для запуска gnome-терминала с другим идентификатором приложения, когда пользователь пропускает игнорируемую теперь опцию --disable-factory. Это должно восстановить совместимость со старыми программами запуска для пользователей, которые обновляются. [...]

Я не могу перемещаться по "Launchpad" в Ubuntu (так много для открытого исходного кода), но скрипт-обертку можно найти в https://launchpad.net/ubuntu/+archive/primary/+files/gnome-terminal_3.14.2-0ubuntu3.debian.tar.xz (называется gnome-terminal.wrap).

Баг в том, что gnome-terminal.wrap скрипт ожидает неправильного дочернего процесса; он должен ждать на терминальном сервере, а не на терминальном клиенте. Исправление состоит в том, чтобы изменить два метода server_appeared а также spawn_terminal_server следующее:

    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)

Вы можете скачать исправленный файл по адресу: https://gist.github.com/ecatmur/00893506a23e828c6688.

Я уведомил сопровождающего пакета, так что, надеюсь, это будет исправлено довольно скоро.


Еще один интересный факт: gnome-терминал может быть собран с альтернативным клиентом под названием gterminal который имеет --wait вариант, который, кажется, делает именно то, что вы хотите. Однако, к сожалению, Ubuntu не собирает и не устанавливает его в свой пакет gnome-терминал.

Для людей в феврале 2017 года

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

работает и запускает gnome-терминал синхронно / блокирующим образом, как и ожидалось.

Проверено в:

  • Ubuntu 16.04
  • гном-терминал 3.18.3

Я использовал более новый gnome-терминал, и поведение, которое вы описали для gnome-терминала, похоже, одинаково для konsole, lxterm и rxvt (пробовал все 3). Так как OP пока не ответил ни на какие комментарии, чтобы уточнить, что он или она хочет, я предполагаю, что OP хочет продолжать использовать родительский терминал, не дожидаясь завершения дочернего терминала.

Это может быть достигнуто с gnome-terminal &, Если вы хотите избежать закрытия дочернего терминала при выходе из родительского режима, используйте nohup gnome-terminal &, Чтобы избежать вывода ошибок при использовании родительского терминала gnome-terminal 2> /dev/null & или же nohup gnome-terminal 2> /dev/null &,

Другие вопросы по тегам