Bash Terminal – Why Exiting Python REPL Clears Ctrl-V Mapping

bashkeyboard shortcutsterminal

I have mapped CtrlV in my .bash_profile to a function that opens a file in Vim using fzf. It works perfectly, but whenever I exit from the Python REPL the mapping doesn't work until I source my .bash_profile twice or start a completely new shell session.

Steps to reproduce:

  1. Use this minimal .bash_profile that replicates the problem:

    stty lnext ^-
    bind -x '"\C-v": "echo mapping works"'
    
  2. Start a Bash session.

  3. Press CtrlV -> see the output mapping works.

  4. Run python3.

  5. Use exit() or CtrlD to exit the REPL.

  6. CtrlV is back to its default behavior.

  7. source ~/.bash_profile the mapping still doesn't work?

  8. source ~/.bash_profile now CtrlV correctly produces mapping works, how?

Why does starting/exiting the Python REPL affect my CtrlV mapping (this does not happen to my other mappings)? And why do I need to source my profile twice for the mapping to start working?


Here's example output of running stty -a | grep lnext:

~ $ stty -a | grep lnext
    lnext = <undef>; min = 1; quit = ^\; reprint = ^R; start = ^Q;
~ $ python3
Python 3.8.2 (v3.8.2:7b3ab5921f, Feb 24 2020, 17:52:18)
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> exit()
~ $ stty -a | grep lnext
    eol2 = <undef>; erase = ^?; intr = ^C; kill = ^U; lnext = ^V;
~ $ source ~/.bash_profile
~ $ stty -a | grep lnext
    lnext = <undef>; min = 1; quit = ^\; reprint = ^R; start = ^Q;
~ $ source ~/.bash_profile
~ $ stty -a | grep lnext
    lnext = <undef>; min = 1; quit = ^\; reprint = ^R; start = ^Q;

Here is some information of my system, python3 is from python.org's installer, and bash is from Homebrew:

~ $ which python3
/usr/local/bin/python3
~ $ python3 --version
Python 3.8.2
~ $ which bash
/usr/local/bin/bash
~ $ bash --version
GNU bash, version 5.0.17(1)-release (x86_64-apple-darwin19.4.0)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
~ $ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.15.5
BuildVersion:   19F101

Here is even more information about my system:

~ $ echo $BASH_VERSION
5.0.17(1)-release
~ $ lsof -p $$
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF                NODE NAME
bash    78982 eero  cwd    DIR    1,5     1568              359056 /Users/eero
bash    78982 eero  txt    REG    1,5   991320             4660332 /usr/local/Cellar/bash/5.0.17/bin/bash
bash    78982 eero  txt    REG    1,5  1568368 1152921500312627066 /usr/lib/dyld
bash    78982 eero    0u   CHR   16,2   0t3107                 985 /dev/ttys002
bash    78982 eero    1u   CHR   16,2   0t3107                 985 /dev/ttys002
bash    78982 eero    2u   CHR   16,2   0t3107                 985 /dev/ttys002
bash    78982 eero  255u   CHR   16,2   0t3107                 985 /dev/ttys002
~ $ /usr/local/bin/bash -li
~ $

Best Answer

I can not reproduce the problem that leaving REPL python changes lnext.
But I can confirm that you need to source a file twice to get the bind key to work (the output of mapping works).

Note that you need to start a logging bash session to get .bash_profile to be read. A file like:

$ cat ~/.bash_profile
echo bash_profile read
#stty lnext ^-
stty lnext ''

Will only work if bash is called like bash -l for example. Just check that you get bash_profile read when you start bash.

Solution:

The best bet is to set the key binding in the file ~/.inputrc:

$ cat ~/.inputrc
#bind -x '"\C-v": "echo mapping works"'
\C-v: "echo mapping works\C-m"

Then, the binding could be read in with Ctrl-x Ctrl-r, nothing else is needed, as soon as that is executed the key binding works in bash. And, it will be read when bash start.

If you still need to remove the same keybinding in the tty:

$ cat ~/.bash_profile
echo bash_profile read
#stty lnext ^-
stty lnext ''

Using lnext '' works more consistently IME. Remove or comment the echo when needed.

Added
There is no way to (exactly) mimic the bind -x way of executing shell commands.

But what is a command ?, but an string that ends with an enter.
Place this on the ~/.inputrc

\C-v: "echo 'yes\!. It works.'\n"

Re-read the ~/.inputrc file and try \C-v.
Note that (IME) it is better to use \C-m.

If you want the line cleared before typing the command, use

\C-v: "\C-e\C-x\C-?echo 'yes\!. It works.'\C-m\C-y"

But, bear in mind that more complex bindings have more probability to fail for unknown reasons. Just try it, maybe it will work for you. If it does, the only difference with the normal bind -x is that the command executed gets into the history (nothing important IMhO).

Related:

Related Question