I happen to find myself regularly in the situation of writing a throw away BASH script. Often, I need to run the script several times before I have it implemented correctly. This is the typical edit-(compile-)run cycle most programmers know. The trial-and-error workflow must be of course as fast as possible. I am already using tmux so switching between the editor and the console where the script runs is pretty fast. Still, I need to switch to that window, recall the previous command and execute it again. The appropriate tmux/shell key sequence would be <tmux-prefix>4<CAPSLOCK>k<ENTER>. Let’s examine this in detail

<tmux-prefix>4 	Jump to the window (in this case window #4) where the script is run.
<CAPSLOCK>	Enter Normal mode in Zsh. I am using vi-mode in the shell and have mapped CAPSLOCK to ESC
k		Scroll up to the previous command. This is the previously executed script, of course.
<ENTER>		Execute the command.

These are still quite a lot of keystrokes for something that is going to be repeated all the time. Luckily, I have two aliases in my alias file list which allow me to run a command in an endless while loop.

# While True Start
alias wts='while true ; do echo WHILE BEGIN $(date); '
# While True End
alias -g wte=' ; echo WHILE EXIT $?; echo WHILE END $(date); read ; done'

The usage is pretty simple:

$ wts echo "runme" wte
WHILE BEGIN Mon May 28 15:05:01 CEST 2018
runme
WHILE EXIT 0
WHILE END Mon May 28 15:05:01 CEST 2018

When I press Enter now, I get the same command repeated again and again. So my edit-run cycle keystrokes are now <tmux-prefix>4<ENTER>. Plus, I don’t have to retype the while loop every time I am in the edit-run cycle.

This works of course only due to the -g feature in Zsh’s alias built-in which allows to define global aliases, i.e. aliases that are expanded not only at the beginning of the command. Typing two distinct aliases is a bit cumbersome. A more intuitive solution would be a shell function:

wtse () {
        while true
        do
                echo WHILE BEGIN $(date)
                $@
                echo WHILE EXIT $?
                echo WHILE END $(date)
                read
        done
}

However, this gets in your way when using more “advanced” shell magic. Watch:

$ wts ls | tee my.output wte
# vs
$ wtse ls | tee my.output

In the second case, the file (my.output) will also contain the additional information that was printed by the function. This was arguably not intended and so keeping the original while loop aliases makes sure you do not have to worry about redirections & pipes.

The date commands serve two purposes. Firstly, they make it visible that the command has run and finished; just in case the execution didn’t output anything. Finally, they allow to get a feeling how long the command took to execute.

There are further ways to shorten the edit-run cycle. One could setup a trigger in the editor to run the command in the background or send the appropriate tmux keys to run it in another window/pane. I haven’t tested these options (yet). I think the trigger-in-the-editor approach is cumbersome to setup because one has to “configure” the editor every time so that it knows what to run. Redirection/pipes might also be a problem.

By “sending the tmux keys” I mean that one could alternatively instruct the editor to tell tmux to run the command in another window/pane. This would of course assume that the other window/pane is prepared for that. I.e. one would have had run the command at least once to be able to send the generic instruction “run again”. If that is the case, a simple tmux send-keys -t "<window>.<pane>" Escape k Enter should do. This assumes that the shell uses vi bindings and <ESC>k<ENTER> scrolls up and executes the last command. Again, here I feel the “burden” of the setup (finding the window/pane or always use the preset ones). Furthermore, it is less convenient to change the actual command being run.