1

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?

1
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)"
| improve this answer | |
  • 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 at 16:24
  • 1
    @wile_e8 see the updated function / script at the end of the answer – mosvy Mar 30 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 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. – mosvy Mar 31 at 18:28
  • 1
    @wile_e8 see the added workaround. I had included it in the external script variant by default. – mosvy Mar 31 at 18:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.