3

In spite of heavy tmux use and switching between panes, I still frequently find myself using shell job control. When something needs poking I'll background the tool I'm using with Ctrl+Z, poke the thing, then fg the tool again. The issue is sometimes the "poking" turns into using another tool and I want to go back and forth a couple times.

Would it be possible to bind another key that is trapped by the shell (i.e. not ever passed though to a foreground app) that backgrounds the current job and foregrounds whatever the most recent background job is?

5
  • 1
    You can't have a key that goes directly to the shell because it isn't reading from the terminal. You can have a tmux binding that sends a succession of keys but it might be tricky because tmux can't control who is reading from the terminal. A few keys are interpreted by the terminal and transmitted to the shell as a signal, e.g. Ctrl-z → SIGSTOP, but you can't define new signals. Are two keys acceptable? For example I've set up my shell so that C-z C-z backgrounds the foreground program, with a stop that's as brief as my ability to press two keys. Commented Oct 25, 2019 at 16:03
  • @Gilles, I'd be happy with C-z C-z backgrounding one job and forgrounding the other, yes. I do wonder if any of the existing signals could be clobbered instead. There are a couple I don't care about, and even have disabled (C-s for example).
    – Caleb
    Commented Oct 25, 2019 at 16:21
  • C-s is a terminal action that doesn't send a signal. The only signals are C-c → SIGINT, C-z → SIGTSTP and C-` → SIGQUIT. If you're happy with C-z C-z`, I suggest that you adapt my function to select whichever foreground job you want. Commented Oct 25, 2019 at 16:27
  • 1
    Giles you linked to a 404. Commented Oct 25, 2019 at 17:05
  • 1
    I think the link should be unix.stackexchange.com/a/10851
    – Mikel
    Commented Oct 29, 2019 at 2:43

1 Answer 1

1

This makes issuing CTRL+Z immediately resume the previous job, as long as you have more than 1 job suspended. The only way to exit the while loop is to close programs until at most 1 backgrounded program is left.

function fgstack() {
  local n_jobs=$(jobs | tr -cd "[:digit:][:space:]" | wc -l)
  while [[ n_jobs -gt 1 ]]; do
    fg %-;
    n_jobs=$(jobs | tr -cd "[:digit:][:space:]" | wc -l)
  done
}

If you want to give yourself a chance to break out from the two-job loop, suspending a job before before 0.2 seconds has passed does it CTRL+Z+Z: (zsh -- for bash you would need to use something else for getting the time):

function fgstack() {
  local n_jobs=$(jobs | tr -cd "[:digit:][:space:]" | wc -l)
  while [[ n_jobs -gt 1 ]]; do
    if [[ $SECONDS -lt 0.2 ]]; then
      break
    fi
    typeset -F SECONDS=0
    fg %-;
    n_jobs=$(jobs | tr -cd "[:digit:][:space:]" | wc -l)
  done
}

Here's another, that requires fzf but gives you a menu if you have more than two options to choose from:

function fgstack() {
  local n_jobs=$(jobs | tr -cd "[:digit:][:space:]" | wc -l)
  while [[ n_jobs -gt 1 ]]; do
    if [[ n_jobs -eq 2 ]]; then
      fg %-;
    else
     job=$(jobs | fzf -0 -1 | tr -cd "[:digit:]") && fg %$job
    fi
    n_jobs=$(jobs | tr -cd "[:digit:][:space:]" | wc -l)
  done

Tested in zsh and bash -- bash seems to exit the while loop after returning from the first backgrounded job after invoking the function, edits & comments are welcome if someone knows how to fix that!

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .