By including stty -a
in the script I discovered that when Bash executes it from the binding, the terminal is configured with -icanon
, -icrnl
and -echo
. Normally, when the script is executed from the command line, these options are icanon
, icrnl
and echo
.
I also read the settings while read
was waiting for input, just to make sure the shell does not re-configure the terminal solely for read
. I did this by executing </dev/pts/… stty -a
in another console (…
substituted with the right value) while read
was waiting. The results were the same.
-icrnl
is the main culprit. See man 1 stty
. This setting is responsible for translating carriage return characters (CR) to newline characters (NL).
Traditionally terminals and terminal emulators send CR upon Enter. It's the same character you get by pressing Ctrl+M. With the icrln
each CR is translated to NL by the line discipline of the tty. Shells and other tools usually expect NL at the end of the line and the translation is exactly what they need to react to Enter as you expect. In particular the read
builtin of Bash needs it.
When you run your script from the binding, the translation does not happen (-icrnl
). Your script will still react to Ctrl+J (try it) because it sends exactly the right character (NL) and no translation is needed.
Somewhat surprisingly, icrnl
is disabled (i.e. it's -icrnl
) when Bash lets you type a command in the command line, after the prompt. icanon
and echo
are also disabled. Bash itself (more specifically: the Readline library Bash uses) interprets CR (and few other things) and it echoes characters back; it does not rely on the line discipline. Interactive Bash configures the terminal to icanon icrnl echo
(plus possibly other settings) when it starts a foreground command (e.g. cat
or /tmp/WEx_tst01.sh
, or read
); interactive Bash configures the terminal back to -icanon -icrnl -echo
for itself when it's time to interactively read a next command.
Apparently Bash does not re-configure the terminal before it calls your script as a result of the binding. -icanon -icrnl -echo
remain, while read
in your script rather expects icanon icrnl echo
.
To make your script react to Enter, it's enough to enable icrnl
just before read
in the script:
stty icrnl # you may also want echo: stty icrnl echo
read …
Alternatively tell read
to use Readline. Readline will configure the terminal accordingly. Note the script has to be interpreted by Bash, so you need the right shebang:
#!/bin/bash
read -ep "type RET to continue"
The shebang is needed not only because of -e
. -p
you used is also not portable. Even your original script needs a shebang, in general it cannot be executed by a random POSIX-compliant shell.
With no shebang, Bash would be used anyway, at least from Bash (including "from a binding in Bash"). The right thing is to use a shebang in any script designed to be executed (as opposed to sourced).
With no shebang, execution of the script (or even a script containing just a comment) from the binding in my Bash 5.1.4 results in icanon
being improperly set after. This may be a bug. The right shebang helps, I don't know why though.
Note not only Bash does not prepare the terminal for the script when it executes the script upon keystroke. Bash also does not restore proper settings when the script exits. stty icrnl
we used will remain (although it will probably be unnoticeable; and it will be fixed after the next Enter or so). You can save the settings before stty
+read
and restore them after. Example:
#!/bin/bash
settings="$(stty -g)"
stty icrnl
read -p "type RET to continue"
stty "$settings"
When the above script runs as a result of the keystroke and I hit Ctrl+C during read
then the last stty
won't be executed. Sometimes I get icanon
after Ctrl+C, despite the fact it was -icanon
during read
; it seems to somewhat depend on what's later in the script (and still not being executed because of Ctrl+C), the results seem inconsistent and I'm not going to investigate further. I get icanon
after Ctrl+C during read -e
as well.
icanon
when Bash returns to the prompt is harmful because it severely cripples our ability to edit the command line.
A workaround may be to tell the line discipline not to treat Ctrl+C as special:
#!/bin/bash
settings="$(stty -g)"
stty icrnl intr ''
read -p "type RET to continue"
echo # so the next prompt will be in the next line
stty "$settings"
I suspect bind -x
was not designed to run commands that read from the terminal and/or change its settings, hence the problems.