# Preface:

I first encountered this with a Node.js script I was trying to simulate a REPL with. I cooked up this demo to prove that is also happens with bash.

Why is it that on subsequent runs of step 9, you get more than just 5 comments?

### Note:

• The lines that begin with : like are intended to be copy-pasted

curl -O https://gist.githubusercontent.com/RichardBronosky/6f936021ed77e77377729d9ed2d1a1f4/raw/vimception.sh

# Code

#!/usr/bin/env bash

echo '##  1. Save this file as vimception.sh                                                                 '
echo '##       :w vimception.sh                                                                              '
echo '##  2. Make it executable                                                                              '
echo '##       :!chmod +x %                                                                                  '
echo '##  3. At the EOF ($), read (r) the result of executing (!) the path (./) to this file (%) ' echo '## :$r!./%                                                                                       '
echo '##  4. Run that a few times and watch the comments stack up (colon followed by up arrow is your friend)'
echo "##  5. See the time $(date '+%Y/%m/%d-%H:%M.%S') and [Sub-]Process ID$(sh -c 'echo $PPID') of each run" ## (Okay, enough with echoing the comments!) ## 6. Now we'll make this script clean up after itself ## 7a. At the EOF ($), put (put) 2 # chars (='##') (This prevents errors by insuring at least 1 match for...)
## 7b. separate command (|) globally (g), where pattern (/^##/) matches, delete the line (d)
##       :$put ='##'|g/^##/d ## 8. Run that a few times to confirm it is idempotent ## 9. Combine steps 7 & 3 to get a fresh set of comments rather an seeing them stack up ## :$put ='##'|g/^##/d|$r!./% ## 10. Run that one more time ## 11. Explain to me what happened  ## 1 Answer I'm not sure to understand, but maybe your issue comes from the global command which doesn't interpret the pipe character as command termination but as a part of its argument. So, when you execute this command: :$put ='##'|g/^##/d|$r!./%  Maybe you think it does 3 things: $put ='##'
g/^##/d
$r!./%  But in fact, it does only 2 things: $put='##'
g/^##/d|$r!./%  In the last command, whenever the global command :g finds a line which begins with 2 number sign characters (#), it executes this command: d|$r!./%


Which means, it deletes the line and it inserts the output of the shell commands (echo) contained in your current script.

If you don't want :g to interpret the pipe as a part of its argument, you could hide it, wrapping the command in a string and executing it:

:$put ='##'|exe 'g/^##/d'|$r!./%


This time, :g will only see what's inside the string (d), it won't execute your shell script every time it finds a line beginning with ##.

I could be wrong, but I think most Ex commands interpret the pipe as command termination. However there are some exceptions, :g is only one of them. I think the following ones also interpret the pipe as a part of their argument:

:argdo, :bufdo, :tabdo, :windo
:autocmd
:command
:function
:[v]global
:make
:normal
:!


I don't remember where I found this list, maybe in the help or in a blog post. There may be other commands which I missed.

Another way of executing a new Ex command after one of these commands, is to type C-v C-j, which should insert a newline or a null character.

So, you have 2 choices:

:{1st command} C-v C-j {2nd command}


or

:execute '{1st command}' | {2nd command}


Edit: I think I found the complete list of commands, it's in :h :bar:

                            *:bar* *:\bar*
'|' can be used to separate commands, so you can give multiple commands in one
line.  If you want to use '|' in an argument, precede it with '\'.

These commands see the '|' as their argument, and can therefore not be
followed by another Vim command:
:argdo
:autocmd
:bufdo
:cdo
:cfdo
:command
:cscope
:debug
:folddoopen
:folddoclosed
:function
:global
:help
:helpfind
:lcscope
:ldo
:lfdo
:make
:normal
:perl
:perldo
:promptfind
:promptrepl
:pyfile
:python
:registers
:scscope
:sign
:tcl
:tcldo
:tclfile
:vglobal
:windo
:write !
:[range]!
a user defined command without the "-bar" argument |:command|

• Wow! That was exactly it. I even read :help global to know how to explain g/ in the comments. I still had no idea that was the issue. Thanks for the excellent explanation in such a short time! – Bruno Bronosky Jan 12 '17 at 21:18
• So, for the real use case (node.js) that had me create this, I used \$put ='//'|exe 'g?^//?d'|w|r!./% The main difference being the addition of a w for writing the file, and use of // comments. I really like this pattern of editing in a single window rather than monkeying with tmux. – Bruno Bronosky Jan 12 '17 at 21:22
• @BrunoBronosky Nice! Yes, I think it's a common pitfall in Vim, you have to be aware that not all Ex commands treat the pipe exactly the same way. – user9433424 Jan 12 '17 at 21:32