Single shortcut to move a running process to the background in bash [duplicate]

I know how to move a process to the background by suspending it first, discussed here and here:

  1. Type Ctrl+Z
  2. Run either %& or bg

However I'd like to do this without interrupting the process too much (not have it paused for ages while I type to start it up again). For example if it's playing audio and I don't want to interfere. It looks like there's no way to actually move it while running (see this and this), but I'll happily accept a single shortcut that does both the above operations in one step.

A similar question is here, but about zsh: How can I do Ctrl-Z and bg in one keypress to make process continue in background?

6

1 Answer

The commands can certainly be combined, but triggering them is difficult.

One option is binding Ctrl-Z in the shell so you can hit it once to suspend and again to bg.

My solution is to set a global shortcut to run a script which does the work. Firstly, the PID of the shell running in the active terminal window is obtained. Then get the process in the foreground, and finally suspend and resume it. The same works if you want to kill it.

An annoying issue is KDE's konsole which has a single process for every terminal window. I found the answer here, or at least it pointed me in the right direction:

Here's the script:

#!/bin/bash
#get the active window
WID=$( xprop -root _NET_ACTIVE_WINDOW | grep -o '0x[0-9a-f]\+' )
#get the parent's process ID (looks like I don't need this as there's only ever one konsole/gnome-terminal process running)
TPID=$( xprop -id $WID _NET_WM_PID | grep -o '[0-9a-f]\+' )
#get the window class, to check what terminal is running
TERM=$( xprop -id $WID WM_CLASS | grep -o 'konsole\|gnome-terminal' ) #change this to match your terminal. only tested with konsole
#quit if a terminal is not active
if [[ -z "$TERM" ]] ; then exit -1
fi
#get bash PID for the window, somehow
if [[ "$TERM" == "konsole" ]] ; then #this seems really really roundabout, but here goes.. #for every konsole window (which may have many tabs/sessions) for WINDOW in $( qdbus org.kde.konsole /Windows org.freedesktop.DBus.Introspectable.Introspect | grep "node name" | grep -o "[0-9]\+" ) ; do #get the "current" session, which I guess is the active tab. seems to work SESSION=$( qdbus org.kde.konsole /Windows/$WINDOW org.kde.konsole.Window.currentSession ) #get the window ID from the environment variables (why is not available via qdbus??) KWID=$( qdbus org.kde.konsole /Sessions/$SESSION org.kde.konsole.Session.environment | grep WINDOWID | grep -o "[0-9]\+" ) #convert it to hex, to match the query at the top KWID=$( printf "0x%x\n" $KWID ) #if this active tab is in the active window, it's the one we want. grab the PID of the shell if [[ "$WID" == "$KWID" ]] ; then SHELL_PID=$( qdbus org.kde.konsole /Sessions/$SESSION org.kde.konsole.Session.processId ) break fi done
else ps --ppid $TPID #<- need to figure out which bash PID for the WID, as above for KDE echo "only works for KDE atm. sorry" exit -1
fi
echo WID=$WID PID=$PID TERM=$TERM SHELL_PID=$SHELL_PID
#find the foreground process
ps -O stat --ppid $SHELL_PID | grep -v PID | while read CHILD ; do if [[ -n "$(echo $CHILD | awk '{print $2}' | grep \+)" ]] ; then PID=$( echo $CHILD | awk '{print $1}' ) kill -SIGSTOP $PID sleep 0.0001 #?? kill -SIGCONT $PID break fi
done

This works just fine. For example,

>>> sleep 100 #press shortcut here
[1]+ Stopped sleep 100
>>> jobs #check it's been restarted
[1]+ Running sleep 100 &
>>> 

However it does rely on the window manager being able to set global shortcuts and being able to get the active terminal and shell, which is pretty damn roundabout. I guess there might also be issues if say ssh is running, not sure.

You Might Also Like