0

I have an application launcher that executes a command inside gnome-terminal with the -- option, but whenever I go into the terminal and try crtl-z to try to suspend the command and move it to the background nothing happens. My search found a bunch of posts about launching interactive shells in bash, but nothing I've tried there works either. Is there way to launch gnome-terminal and execute a command that gives me the ability to suspend the command and bring up a prompt later?

wile_e8
  • 111

1 Answers1

2
bash_in_gnome_terminal(){
    local IFS
    printf -v cmd %q ". ~/.bashrc; set -m; $*"
    gnome-terminal -- bash -c "bash --rcfile <(echo $cmd)"
}

bash_in_gnome_terminal tail -f ~/.xsession-errors
bash_in_gnome_terminal 'grep -r pattern dir | less'

This solution is complicated (and crippled!) by the fact that the gnome-terminal command actually calls into a "terminal server" to spawn a terminal instance, and you cannot pass file descriptors to the process running in it.

With other terminals like xterm or mlterm the solution is simpler and more functional:

bash_in_xterm(){
    local IFS
    xterm -e bash --rcfile <(printf '. ~/.bashrc; set -m; %s\n' "$*")
}

Also, it would be nice if bash had an option to run some commands before an interactive shell (like vi +'cmd'), without a kludge like --rcfile <(...). Maybe it even has -- but I wasn't able to figure it out ;-)

The set -m is needed because bash sources the initialization files with the monitor mode off, ie without job control and the possibility of using ^Z, fg, bg, etc.


If you want the shell to exit and the terminal to close when the started command has exited, the function could be modified like this [assumes a recent version of bash]:

bash_in_gnome_terminal(){
    local IFS
    set -- 'sigchld(){ local j="\j"; ((${j@P})) || exit; }; trap sigchld CHLD;' "$@"
    printf -v cmd %q ". ~/.bashrc; set -m; $*"
    gnome-terminal -- bash -c "bash --rcfile <(echo $cmd)"
}

The ${var@P} form expands any prompt escapes in var, and the \j prompt escape expands to the number of jobs. Thence j="\j"; ((${j@P})) || exit as called from the CHLD trap will exit the shell if there are no more jobs.

In older version of bash which do not support the ${var@P} expansion form, a clunkier kludge will have to be used:

set -- 'sigchld(){ trap - CHLD; test "$(jobs -p)" || exit; trap sigchld CHLD; }; trap sigchld CHLD;' "$@"

Here the CHLD trap has to be disabled and then reenabled, otherwise the $(jobs -p) command substitution will trigger it recursively.


This whole thing could be made into a standalone script instead of a function:

#! /bin/bash

# uncomment and edit the following line accordingly
# set -- <fixed command and arguments> "$@"

# this only works in bash >= 4.4
# set -- 'sigchld(){ local j="\j"; ((${j@P})) || exit; }; trap sigchld CHLD;' "$@"
set -- 'sigchld(){ trap - CHLD; test "$(jobs -p)" || exit; trap sigchld CHLD; }; trap sigchld CHLD;' "$@"

printf -v cmd %q ". ~/.bashrc; set -m; $*"
gnome-terminal -- bash -c "bash --rcfile <(echo $cmd)"
  • I created a script with your gnome-terminal answer, modified to call my commands from the function at the end. This pretty much did what I wanted, with the only downside being that the terminal doesn't close when I exit the command like the old gnome-terminal launcher did. But still a great improvement! – wile_e8 Mar 30 '20 at 16:24
  • 1
    @wile_e8 see the updated function / script at the end of the answer –  Mar 30 '20 at 17:48
  • I tried the updated script (both versions), but kept getting the same errors at the end instead of exiting:
        bash: ${j@P}: bad substitution
    

    This would appear 2-3 times after I exited instead of the terminal closing, and then if I ran any commands in the terminal it would appear 2-3 times after each of those commands.

    – wile_e8 Mar 31 '20 at 18:26
  • 1
    @wile_e8 that form of variable expansion is only present in newer versions of bash (>= 4.4 or so). I'll add a workaround -- which would be quite clunky, unfortunately. –  Mar 31 '20 at 18:28
  • 1
    @wile_e8 see the added workaround. I had included it in the external script variant by default. –  Mar 31 '20 at 18:41
  • Works perfectly, thanks! – wile_e8 Mar 31 '20 at 18:52