tmux and It's Extension smux

tmux is short for Terminal MUltipleXer and, conceptually, lets you "split" one terminal into many different terminals. It is a very powerful program with a lot of different features, below I have simply listed two resources I've found helpful.

  • tmux Cheat Sheet for remembering how to do certain things
  • tmux Wiki on GitHub is helpful for learnign the vocabulary and how to use it

One of the features that tmux offers is the ability to "detach" from a terminal session and allow the program to continue running in the background. This is very helpful when connected with ssh, allowing us to go to the cluster, start a program that takes a long time to run, detach from that terminal, disconnect from ssh, and only reconnect when we want to check on our long-running program. This ssh+tmux workflow is so common for me that I wrote a small bash wrapper connecting the two called smux.

Below, I have copied the bash code which I've been using for a few years now. Feel free to put it in your .bashrc or some other file for later use. I've also isolated it into a POSIX-compliant shell executable on GitHub which is light enough to install into your home directory pretty much anywhere.

curl -s https://raw.githubusercontent.com/tomeichlersmith/smux/main/install | sh 

smux

#!/bin/bash

# ssh+tmux = smux

__smux_help() {
  cat <<\HELP

  Using ssh and tmux on remote computers.

  NOTE: <TAB> completion for smux only works after it is attempted for ssh.

 USAGE: 
  smux [-h|--help] [-l|--list] <host> [session]

 OPTIONS:
  -h|--help : print this help and exit
  -l|--list : list the sessions on the input host and exit

 ARGUMENTS:
  host      : (required) hostname of computer you wish to attach to
  session   : (optional) name of session to attach to on the input host 

HELP
}
__smux_list() {
  local _host="$1" #required
  ssh -t ${_host} "tmux ls 2> /dev/null"
  return $?
}
__smux_attach() {
  local _host="$1" #required
  local _session="$2" #optional
  ssh -t ${_host} "tmux attach ${_session:+-t} ${_session} || tmux new ${_session:+-s} ${_session}"
  return $?
}
smux() {
  case "$1" in
    ""|-h|--help)
      __smux_help
      return 0
      ;;
    -l|--list)
      __smux_list "$2"
      return 0
      ;;    
    -*)
      echo "ERR: Unknown option $1"
      return 1
      ;;
  esac
  
  __smux_attach "$1" "$2"
  return $?
}

__complete_smux() {
  if ! hash _ssh &> /dev/null; then
    # __load_completion is a bash internal that is used
    #   to expand the tab completion set of functions if
    #   no function is defined
    #   it defines the ssh completion function _ssh
    __load_completion ssh || return $?
  fi 

  # disable readline filename completion
  compopt +o default

  local __curr_word="${COMP_WORDS[$COMP_CWORD]}"

  if [[ "$COMP_CWORD" == "1" ]]; then
    if [[ "${__curr_word}" == -* ]]; then
      # option
      COMPREPLY=($(compgen -W "-h --help -l --list" -- "${__curr_word}"))
    else
      # host, use ssh tab completion
      _ssh
      return $?
    fi
  else
    case "${COMP_WORDS[1]}" in
      -l|--list)
        # shift inputs so _ssh tab complete works
        COMP_WORDS=(${COMP_WORDS[@]:1})
        COMP_CWORD=$((COMP_CWORD - 1))
        _ssh
        return $?
        ;;
      *)
        # no other tab complete implemented,
        #   we /could/ try tab completing tmux sessions on the host
        #   that is already selected, but that would make tab complete
        #   slow since it would have to ssh there to find out the list
        COMPREPLY=()
        ;;
    esac
  fi
}
complete -F __complete_smux smux