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.

muru
  • 72,889

2 Answers2

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:

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 &quot;$strOpts&quot; |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 &amp;&amp; $bSubShell;then
  bSubShell=false
  echo &quot;WARN: disabling subshell mode to let bUseBashAutoCmdMode work for now.&quot; &gt;&amp;2
fi

if $bSubShell;then
  strCmd=&quot;bash -c ${strFl}&quot;
else
  # can the command passed thru qdbus param cause some problem if it is too complex? `bash -c` above is to prevent that.
  strCmd=&quot;`declare -p acmdRun`;&quot;'&quot;${acmdRun[@]}&quot;'
  bForceIgnoreStart=true
fi

if $bUseBashAutoCmdMode;then
  #echoc --alert &quot;@{-n}execute_command_by_uuid was @nremoved@{-n}, therefore this script wont work that way anymore :(&quot;;
  #exit 1
  strExecFile=&quot;`bashAutoCmdOnStart.sh --getExecFileForUID &quot;$strTabUUID&quot;`&quot;
  echo &quot;$strCmd&quot; &gt;&gt;&quot;$strExecFile&quot;
  while [[ -f &quot;$strExecFile&quot; ]];do
    #echo &quot;INFO: Waiting '$strExecFile' be consumed...&quot;
    read -p &quot;(`date`)INFO: Waiting '$strExecFile' be consumed... skip waiting? (y/...)`echo -ne &quot;\r&quot;`&quot; -t 1 -n 1 strResp&amp;&amp;:;if [[ &quot;$strResp&quot; == &quot;y&quot; ]];then break;fi
    sleep 1
  done
else
  # this is for old guake version
  if [[ &quot;$strTabUUID&quot; == &quot;USE_CURRENT_TAB&quot; ]];then
    FUNCgrc execute_command &quot;$strCmd&quot;
  else
    FUNCgrc execute_command_by_uuid &quot;$strTabUUID&quot; &quot;$strCmd&quot;
  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 || [[ &quot;$strCurrentTabUUID&quot; == &quot;$strTabUUID&quot; ]];then
    echo &quot; &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; &quot;
    echo &quot; &gt;&gt;&gt;&gt;&gt; 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.&quot;
    echo &quot; &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; &quot;
    bKeepTmpExecFile=true
  else
    nW=0
    while [[ ! -f &quot;${strFl}.started&quot; ]];do
      echo &quot; &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; &quot;
      echo &quot;     &gt; `date`: Waiting ($((nW++))) the command start running:&quot;
      echo &quot;     &gt; TAB: strTabName='${strTabName}',strTabUUID='$strTabUUID'&quot;
      echo &quot;     &gt; CMD$iIdCmd: '${acmdRun[*]}'&quot;
      echo &quot;     &gt; It may fail if there was some command typed and not executed yet there.&quot;
      echo &quot;     &gt; It may fail if there happened an input request there.&quot;
      echo &quot;     &gt; So you better check if no problem happened!&quot;
      echo &quot;     &gt; But if no problem happened and you force a retry, it will be buffered and will execute again.&quot;
      echo &quot;     &gt; Force retry run now? (y/...)&quot;
      echo &quot; &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; &quot;
      strResp=&quot;$(read -t 3 -n 1 strResp;echo &quot;$strResp&quot;)&quot;;echo #the script was stopping here... why did I have to subshell?
      if [[ &quot;$strResp&quot; == &quot;y&quot; ]];then echo &quot; &gt;&gt;&gt;&gt;&gt; Retrying&quot;;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