2

The shell I use is GNU bash, version 6.2.15(1)-release (i686-pc-linux-gnu).

I always use following code to stop a script running:

read -p "type RET to continue"

I have a bash script named

/tmp/WEx_tst01.sh

This script contains two lines

showkey -k
read -p "type RET to continue"

The permissions are set by

chmod a+x /tmp/WEx_tst01.sh

The behavior is the same if the script is called sourced by prepending ". " or not. Thus I describe only the example if not.

case 1:

The script is called directly like

/tmp/WEx_tst01.sh

case 2:

The filename is mapped to the key sequence '''Alt-R''' = '''M-r''' by shell built-in "bind", like

bind -x '"\er":"/tmp/WEx_tst01.sh"'

By

bind -X

the correct binding is apprved.

The results:

case 1 works normally, i.e. if I hit RETURN = '''Enter''' key, the halted script continues.

case 2 does not work. The script does not continue. As if I had not hit RETURN = '''Enter''' key. As if the read function would not react to incoming characters.

And indeed other tests prove that the read function ignores incoming caracters but continues to wait for input.

Yet the script receives the typed RETURN = '''Enter''' key. That is approved by the showkey in the first line.

Please what is the reason, how can I overcome this?

Regards

1 Answer 1

1

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.

7
  • == 04.05.2023 10:51 to Kamil Maciorowski :: your very, very good answer, many, many thanks;, Regards. Commented May 4, 2023 at 8:52
  • @AntonWessel You said "very good answer", but you did not accept it, as if you're waiting for a better answer to appear. What is missing in my answer? Maybe I can improve it. What more do you need? Commented May 15, 2023 at 6:27
  • == 16.05.2023 22:10 the very, very good answer I have read, and I am happy. But I am too stupid, I do not understand "accept", I have no knowledge about such procedure, I would like to do "accept", but how, perhaps in former times I also showed similar misbehaviour. Regards. Commented May 16, 2023 at 20:18
  • @AntonWessel meta.stackexchange.com/a/5235/355310 Commented May 16, 2023 at 20:34
  • @Anton Another hint: there is no need for timestamps in posts or comments. The site keeps a timestamp for each edit or a comment. Commented May 18, 2023 at 17:14

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .