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 Answers
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)"
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