1

My goal is to start a multi terminal environment with:

  • easy access to all terminals at once, like pressing a keyboard key (I can do that with yakuake and guake)
  • custom named tabs so I know where things are
  • start the multi-tab terminal with pre-executed full commands, so I can have a fully set environment for each tab terminal

What I do today?
I open guake, that uses less RAM than yakuake.
I configured some keybinds on bash, so I can hit a key like F5 and start a base environment for developing, so I still have to hit the F5 key for each specific terminal.
And I still have to type on each of the 5 tabs, the specific command for them.
I have also previously named them so I know on which to run what specific command.

Research:

  • Multi-tab terminals: yakuake, guake, mrxvt.
  • Renamable tabs: guake, yakuake, but I cant find a way for mrxvt.
  • I know we can use qdbus with yakuake for many things, like change the tab name. Not sure about guake. Cant find a way do change tab name with mrxvt.

An indirect way (no need to be a command line, could be thru qdbus) to achieve the final result would suffice also, I would just need to prepare some scripts probably.

The one that uses lowest RAM (mrxvt) is also important to cope with the other apps being run, but there is no point if I cant accomplish my goal using it.

4
  • Do you want a normal terminal or do you require it to be a dropdown terminal like yakuake and guake? Would something like this be what you're after: How to open terminal, split to 9 terminals and switch between them using one script? Commented Feb 27, 2022 at 18:54
  • I need the access to all terminals happen at once, like pressing a key. From there I need to see at least the tab name to chose which one I need to use. It could be small terminals all visible at once, but I prefer to see only one terminal per time, that's why I prefer tabs, I need big letters to not strain my eyes. I saw terminator there, installing now to test, thx! Commented Feb 27, 2022 at 19:00
  • I do everything except browsing in a terminal, so I always have a shortcut to launch my favorite terminal. If you set up a default terminator profile as you want it, then it is very easy to bind that to a specific shortcut key. You should probably also check out gnome-terminal for tabs. Commented Feb 27, 2022 at 19:14
  • 1
    See also: How to run xfce-terminal with different commands per tab and keep using the tabs after the commands have returned? Commented Mar 1, 2022 at 16:09

2 Answers 2

1

If you are looking for "multi terminal" environment, I recommend, instead of looking for a terminal itself (Terminator is a good option) check instead the path of terminal multiplexers like tmux.

With tmux you will have all the goodies of the more advanced terminals but you can use it in any terminal and you can use it also in your remote servers.

tmux has also the advantage that if you close the terminal, your TMUX session still is there and you can recover it and all your vim files, o programs and ssh sessions are still running.

IN order to configure several terminal with predefined TABS (windows in tmux) and run automatically some commands, tmux has tmuxinator (https://github.com/tmuxinator/tmuxinator) and tmuxp (https://tmuxp.git-pull.com/)

Example with tmuxp

session_name: 4-pane-split
start_directory: ./ # load session relative to config location (project root).
before_script: ./.tmuxp-before-script.sh
shell_command_before:
  - '[ -f .venv/bin/activate ] && source .venv/bin/activate && reset'
windows:
  - window_name: dev window
    layout: tiled
    shell_command_before:
      - cd ~/ # run as a first command in all panes
    panes:
      - shell_command: # pane no. 1
          - cd /var/log # run multiple commands in this pane
          - ls -al | grep \.log
      - echo second pane # pane no. 2
      - echo third pane # pane no. 3
      - echo forth pane # pane no. 4
  - window_name: docs
    layout: main-horizontal
    options:
      main-pane-height: 35
    start_directory: docs/
    panes:
      - focus: true
      - pane
      - make serve
      - make SPHINXBUILD='poetry run sphinx-build' watch

some links:

1
  • the example has to be in a config file? to use like tmuxp load ...? Commented Feb 27, 2022 at 20:31
0

You will need this if your guake is version >= 3.8.5-1:
cat >>bashAutoCmdOnStart.sh (and add this command to the end of ~/.bashrc)

#!/bin/bash

strFlBase="/tmp/`basename "$0"`."

if [[ "$1" == "--getExecFileForUID" ]];then
  shift
  echo "Showing id for current term on guake:" >&2
  echo "${strFlBase}$1"
  exit 0
fi

#TODO for yakuake too
if [[ -z "$GUAKE_TAB_UUID" ]];then declare -p GUAKE_TAB_UUID;exit 0;fi

: ${bBashAutoCmdOnStart:=true} #help
if ! $bBashAutoCmdOnStart;then ps;pwd;declare -p bBashAutoCmdOnStart;exit 0;fi

ps;pwd
strFlExec="${strFlBase}$GUAKE_TAB_UUID"
nCount=0
while true;do
  echo -ne "$SECONDS: wating terminal auto cmd ($strFlExec). Hit any key for prompt.\r";
  #TODO test if terminal is interactive, I think `read` is failing when guake loads initially on pc restart
  if read -t 3 -n 1;then break;fi;
  if [[ -f "$strFlExec" ]];then
    echo "EXECUTING file contents '$strFlExec' here:"
    cat "$strFlExec"
    strRunFl="${strFlExec}.Running.${nCount}"
    mv -v "$strFlExec" "$strRunFl"
    #chmod +x "${strFlExec}.Running"
    source "${strRunFl}"
    ps;pwd
    #bash -c "${strFlExec}.Running;ps;pwd;bash"
    ((nCount++))&&:
  fi
done
ps;pwd
bBashAutoCmdOnStart=false bash

run example: guakeAutoEnv.sh [-x] -ID_CMD workA ls 123 -ID_CMD workB ls 456 789 #-x is to run it from a xterm (instead of current guake tab) cat >>guakeAutoEnv.sh

#!/bin/bash

nWId="`wmctrl -l |grep "Guake!" |awk '{print $1}'`";declare -p nWId

: ${bUseBashAutoCmdMode:=false}
if ! qdbus org.guake3.RemoteControl /org/guake3/RemoteControl |grep execute_command_by_uuid;then
  #echoc --alert "@{-n}execute_command_by_uuid was @nremoved@{-n}, therefore this script wont work that way anymore :(";
  bUseBashAutoCmdMode=true
fi

if [[ -z "${1-}" ]] || [[ "${1}" == --help ]];then
  #help Usage ex.: [-x] -ID_CMD coolName ls 123 -ID_CMD funnyName ls 456 789
  egrep "[#]help" $0 |sed -r -e 's"^[[:space:]]*""' -e 's"[[:space:]]*$""' >/dev/stderr
  exit 0
fi

export bUsingXtermToRun=false;
if [[ "${1-}" == "-x" ]];then 
  bUsingXtermToRun=true;
  shift&&:;
  (xterm -e $0 "$@"&disown) #w/o -x now
  exit
fi

set -Eeu

# depends on
while ! pgrep -fa "`which guake`";do echo " >>>>> WARN:WaitingGuakeStart";sleep 3;done #this line checks for a running guake
while ! qdbus org.guake3.RemoteControl;do echo " >>>>> WARN:WaitingGuakeDBus";sleep 3;done #this line checks for a listening guake

function FUNCexecEcho() {
  echo " EXEC: $@" >/dev/stderr
  "$@"
};export -f FUNCexecEcho

function FUNCgrc() {
  FUNCexecEcho qdbus org.guake3.RemoteControl /org/guake3/RemoteControl org.guake3.RemoteControl."$@"
}

# read parameters
acmd=()
astrIdList=()
astrOptList=()
iIdIndex=0
function FUNCchkCmd() { if [[ "${1:0:7}" == "-ID_CMD" ]];then return 0;else return 1;fi; } # <"$1">
while ! ${1+false};do
  if FUNCchkCmd "${1}";then
    echo " >>> NEW ID_CMD iIdIndex=$iIdIndex:"
    astrOptList+=("${1#-ID_CMD}");shift
    astrIdList+=("$1");shift;
    continue;
  fi
  
  acmd+=("$1");shift
  if ${1+false} || FUNCchkCmd "${1}";then
    echo "astrOptList[$iIdIndex]='${astrOptList[$iIdIndex]}'"
    echo "astrIdList[$iIdIndex]='${astrIdList[$iIdIndex]}'"
    eval 'aIdCmd'${iIdIndex}'=("${acmd[@]}")';declare -p aIdCmd${iIdIndex}
    ((iIdIndex++))&&:
    acmd=()
  fi
done
declare -p astrIdList

# detect existing tabs
iTabCount="`FUNCgrc get_tab_count`"
iCurrentTabIndex="`FUNCgrc get_selected_tab`"
strCurrentTabUUID="`FUNCgrc get_selected_uuidtab`"
astrTabUUIDList=()
astrTabNameList=()
for((i=0;i<iTabCount;i++));do
  echo
  declare -p i
  strTabName="`FUNCgrc get_tab_name $i`";declare -p strTabName
  if((i==iCurrentTabIndex));then echo " CURRENT TAB";fi
  
  FUNCgrc select_tab $i
  astrTabUUIDList+=("`FUNCgrc get_selected_uuidtab`")
  astrTabNameList+=("`FUNCgrc get_tab_name $i`")
done
FUNCgrc select_tab $iCurrentTabIndex # this first is reached fast so the user has not to wait a long time
: ${bGoBackToInitialTab:=false} #help will re-select select the tab this script was run on at the end

declare -p astrTabUUIDList |tr '[' '\n'
declare -p astrTabNameList |tr '[' '\n'

function FUNCrunCmd() {
  strOpts="$1";shift
  strTabName="$1";shift
  strTabUUID="$1";shift
  iIdCmd="$1";shift

  aOptList=();if [[ -n "$strOpts" ]];then aOptList+=(`echo "$strOpts" |sed -r 's@(.)@\1 @g'`);fi
  
  echo
  echo " TabName: ${strTabName}, CMDOPTION(s): ${aOptList[@]} "
  
  eval 'acmdRun=("${aIdCmd'${iIdCmd}'[@]}")';declare -p acmdRun
  
  bSubShell=true
  bForceCurrentTab=false
  bRetryOnError=false
  for strOpt in "${aOptList[@]}";do
    case "$strOpt" in
      n)bSubShell=false;; #help CMDOPTION: The default is to run using `bash -c '$cmd'`. To disable this, use like ex.: `-ID_CMDn cd ComePathToApplyAtCurrentShell`
      c)bForceCurrentTab=true;if ! $bUsingXtermToRun;then echo " >>>>> WARNING <<<<< 'c' CMDOPTION is more reliable using '-x' option!!!";fi;; #help CMDOPTION: The tab name will only be ignored (but only if it does not exist ex.: "IgnoredCurrentTabName") and the current tab running this script will be used. This option is more reliable with -x option.
      r)bRetryOnError=true;; #help CMDOPTION: if the command fails returning any error, it will retry running it after 3s.
      *)$0 --help;echo "ERROR: invalid option '$strOpt' for strTabName='${strTabName}' and command: ${acmdRun[*]}";exit 1;;
    esac
  done
  
  # create and run a tmp script
  strFl="`tempfile`";chmod +x "$strFl"
  echo '#!/bin/bash
    cat $0; # review the command
    '"$(declare -p acmdRun)"'; # '"${acmdRun[@]}"'
    echo >'"${strFl}.started"'
    if '$bRetryOnError';then
      while ! "${acmdRun[@]}";do echo "failed error $?";sleep 3;done
    else
      "${acmdRun[@]}";
    fi
    ' >"$strFl"
    
  while true;do
    bForceIgnoreStart=false
    
    if $bUseBashAutoCmdMode && $bSubShell;then
      bSubShell=false
      echo "WARN: disabling subshell mode to let bUseBashAutoCmdMode work for now." >&2
    fi
    
    if $bSubShell;then
      strCmd="bash -c ${strFl}"
    else
      # can the command passed thru qdbus param cause some problem if it is too complex? `bash -c` above is to prevent that.
      strCmd="`declare -p acmdRun`;"'"${acmdRun[@]}"'
      bForceIgnoreStart=true
    fi
    
    if $bUseBashAutoCmdMode;then
      #echoc --alert "@{-n}execute_command_by_uuid was @nremoved@{-n}, therefore this script wont work that way anymore :(";
      #exit 1
      strExecFile="`bashAutoCmdOnStart.sh --getExecFileForUID "$strTabUUID"`"
      echo "$strCmd" >>"$strExecFile"
      while [[ -f "$strExecFile" ]];do
        #echo "INFO: Waiting '$strExecFile' be consumed..."
        read -p "(`date`)INFO: Waiting '$strExecFile' be consumed... skip waiting? (y/...)`echo -ne "\r"`" -t 1 -n 1 strResp&&:;if [[ "$strResp" == "y" ]];then break;fi
        sleep 1
      done
    else
      # this is for old guake version
      if [[ "$strTabUUID" == "USE_CURRENT_TAB" ]];then
        FUNCgrc execute_command "$strCmd"
      else
        FUNCgrc execute_command_by_uuid "$strTabUUID" "$strCmd"
      fi
    fi
    
    sleep 1 #wait it start running
    
    : ${bWaitStart:=true} #help wait the command start running before sending the next one
    bForceRetry=false
    bKeepTmpExecFile=false
    bReallyWaitStart=$bWaitStart;if $bForceIgnoreStart;then bReallyWaitStart=false;fi
    if $bReallyWaitStart;then
      if $bForceCurrentTab || [[ "$strCurrentTabUUID" == "$strTabUUID" ]];then
        echo " >>>>>>>>>>>> "
        echo " >>>>> ATTENTION!!! this command '${acmdRun[*]}' was requested to be run on this current tab [$iCurrentTabIndex]'${strTabName}'($strCurrentTabUUID), so this script will not wait for the command to start and the related temporary exec file '${strFl}' will be kept."
        echo " >>>>>>>>>>>> "
        bKeepTmpExecFile=true
      else
        nW=0
        while [[ ! -f "${strFl}.started" ]];do
          echo " >>>>>>>>>>>> "
          echo "     > `date`: Waiting ($((nW++))) the command start running:"
          echo "     > TAB: strTabName='${strTabName}',strTabUUID='$strTabUUID'"
          echo "     > CMD$iIdCmd: '${acmdRun[*]}'"
          echo "     > It may fail if there was some command typed and not executed yet there."
          echo "     > It may fail if there happened an input request there."
          echo "     > So you better check if no problem happened!"
          echo "     > But if no problem happened and you force a retry, it will be buffered and will execute again."
          echo "     > Force retry run now? (y/...)"
          echo " >>>>>>>>>>>> "
          strResp="$(read -t 3 -n 1 strResp;echo "$strResp")";echo #the script was stopping here... why did I have to subshell?
          if [[ "$strResp" == "y" ]];then echo " >>>>> Retrying";bForceRetry=true;break;fi
        done
      fi
    fi
    
    if ! $bForceRetry;then break;fi
  done
  
  : ${bRmTmpFiles:=false} #help delete temporary files
  if $bRmTmpFiles && ! $bKeepTmpExecFile;then rm -v "${strFl}" "${strFl}.started";fi
};export -f FUNCrunCmd

# run the commands
for iId in "${!astrIdList[@]}";do
  declare -p iId
  echo " Id: ${astrIdList[iId]}"
  
  bFound=false
  
  for iTab in "${!astrTabNameList[@]}";do
    strOpt="${astrOptList[iId]}"
    bRunOnThisTab=false
    if [[ "${astrTabNameList[iTab]}" == "${astrIdList[iId]}" ]];then bRunOnThisTab=true;fi
    if ((iTab==iCurrentTabIndex)) && [[ "$strOpt" =~ .*c.* ]];then bRunOnThisTab=true;fi
    if $bRunOnThisTab;then
      FUNCrunCmd "$strOpt" "${astrIdList[iId]}" "${astrTabUUIDList[iTab]}" $iId
      bFound=true
      break
    fi
  done
  
  : ${bAddTab:=true} #help if it doesnt exist yet
  if ! $bFound;then
    if $bAddTab;then
      FUNCgrc add_tab "$HOME"
      FUNCgrc rename_current_tab "${astrIdList[iId]}"
      astrTabNameList+=("${astrIdList[iId]}")
      astrTabUUIDList+=("`FUNCgrc get_selected_uuidtab`")
      FUNCrunCmd "${astrOptList[iId]}" "${astrIdList[iId]}" "USE_CURRENT_TAB" $iId
    else
      echo " >>>>>>>>>>>> "
      echo " >>>>> WARNING: could not find a matching tab name for '${astrIdList[iId]}'"
      echo " >>>>>>>>>>>> "
    fi
  fi
done #for iId in "${!astrIdList[@]}";do

if $bGoBackToInitialTab;then FUNCgrc select_tab $iCurrentTabIndex;fi
0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.