I'm self-answering this question because I've seen it asked all over the Internet, but with few helpful answers, and definitely no resolutions on Stack Overflow that I can find.
Example Code
Consider this code, which simply writes several lines of shell commands:
^0::
SetKeyDelay, 0, 0
myWindow = ahk_exe Notepad.exe
ControlSend, , set c=".cshrc-andrew.cheong"`n, %myWindow%
ControlSend, , set v=".vimrc-andrew.cheong"`n, %myWindow%
ControlSend, , foreach d ( /userhome/andrew.cheong /home/$USER /data/$USER )`n, %myWindow%
ControlSend, , if ( -e $d/$c ) source $d/$c`n, %myWindow%
ControlSend, , if ( -e $d/$v ) alias vim "vim -N -u $d/$v"`n, %myWindow%
ControlSend, , end`n, %myWindow%
Return
I'm writing the commands to Notepad to show that it is not an issue limited to terminal applications like PuTTy or xterm. It's easy to think so, since these applications sometimes have laggy behavior.
Example Output
Specifically when using SetKeyDelay, 0, 0 for fast "typing," I get all kinds of weird behavior, like:
Double-quotes replaced by single-quotes. Parentheses replaced by 9's and 0's. Dollar signs replaced by 4's.
set c=".cshrc-andrew.cheong' set v=".vimrc-andrew.cheong" foreach d ( /userhome/andrew.cheong /home/$USER /data/$USER ) if 9 -e 4d/4c 0 source 4d/4c if ( -e $d/$v ) alias vim 'vim -n -u 4d/4v' endDashes replaced by underscores. Case changes.
set c='.cshrc-andrew.cheong" set v='.vimrc-andrew.cheong' foreach d ( /userhome/andrew.cheong /home/4user /data/$USER ) if 9 -e 4d/$C ) source 4d/$c if 9 _e $d/$v ) alias vim 'vim -N -u $d/$v" endPeriods replaced by >'s. More case changes.
set c=".cshrc-andrew.cheong" set v=">VIMrc-andrew.cheong" foreach d ( /userhome/andrew.cheong /home/$USER /data/$USER ) if 9 -e $d/$c ) source 4d/$c if ( -e $d/$V ) alias vim "vim -N -u $d/$v" end
It's obvious the issue has something to do with the Shift modifier, as if it's being randomly turned on or off.
Why is this happening, and how do we fix it?
Note that there are no problems when using Send and its variants. The issue specifically arises with ControlSend, which is needed to send inputs to specific controls or to an unfocused window.