trap "stty echo" INT has no effect when used with read -s - bash

Given this bash script:
stty -echo
echo $(stty)
reset() {
stty echo
echo $(stty)
exit
}
trap reset int
read -s
sleep 10
I expected the echo option to be enabled, but after pressing ctrlc it is still disabled, even though I have ran stty echo (as you can see in the output by the reset function).

As #KamilCuk has noticed in the comments, read saves the configuration and restores it when the process exists. That results in modifications done while read was running being discarded. The solution is to restore the defaults before running read and redoing them after read finishes.
stty -echo
echo $(stty)
reset() {
stty echo
echo $(stty)
exit
}
trap reset int
stty echo # This line has been added
read -s
echo read finished
stty -echo # This line has been added
sleep 10
#JonathanLeffler also noted that
It can be useful to use old=$(stty -g) to capture the current terminal settings, and then use stty "$old" to reinstate those settings.
Explaination:
Using it allows you to reinstate the exact terminal settings when stty -g was invoked. This can be more reliable than undoing changes with stty -echo etc.
Which I think is the more proper solution, as the stty may run in no-echo mode by default.

Related

Can someone explain to me what stty does?

I have a shell script inside Docker. This is not mine, unfortunately I get the following error when running it. But only under Ubuntu 20.04 and Docker 19.03.
stty: 'standard input': Inappropriate ioctl for device
Here a cutout of line 126 from the script. What makes the stty echo and stty -echo? And why does the script run without problems on an system without docker?
askNoEcho() {
PROMPT=$1
DEFAULT=$2
stty -echo
ask "$PROMPT" "$DEFAULT"
stty echo
echo ""
}
askNonBlankNoEcho() {
PROMPT=$1
DEFAULT=$2
while true; do
stty -echo
ask "$PROMPT" "$DEFAULT"
stty echo
echo ""
if [[ -n "$response" ]]; then
break
fi
echo "A non-blank answer is required"
done
}
stty sets the terminal modes on the terminal connected to stdin. If stdin is not a terminal (eg, it's been redirected from a file when running your script), then it will display the error you show
stty -echo turns off echoing of input keystrokes. Normally when you type in a terminal, the characters you type are echoed back so you can see what you typed. stty echo turns echoing back on. The net effect is to disable echoing for the line entered in response to the prompt -- so it will print a prompt and wait for input, (and return the input), but that input will not be visible on the screen. This is commonly done for entering a password or passphrase.
There are many other terminal modes and settings that can be changed or queried with stty with various arguments. The manual page (man stty) will give you lots of additional info.
Try
$ stty echo < ~/tmp/some.file
stty: standard input: Inappropriate ioctl for device
so, stty expects its standard input to be a terminal.
Therefore, if you run on your terminal
$ stty echo
you will receive no errors.

bash read builtin does not echo input if script is piped to less

I stumbled upon this strange behavior of the bash builtin read.
I have a interactive script which has the potential of generating a large output. So naturally you append | less to it.
The script will still ask you for your input but it will not echo what you typed.
Here is a small sample.sh:
#!/bin/bash
echo "Type:"
read -r input
echo "Typed: ${input}"
sample.sh | less
I noticed that this is not a general issue with pipes (e.g. |cat works).
Any clue would be appreciated.
A SOLUTION which works for me:
#!/bin/bash
STTY_ORIG="$(stty -g)" # save stty settings
stty echo # enable echo
echo "Type:"
read -e -r input # use readline (backspace will not work otherwise)
echo "Typed: ${input}"
stty "${STTY_ORIG}" # restore stty settings
A SOLUTION which works for me and did not show an side effects.
Basically just tweak and restore the terminal settings...
#!/bin/bash
STTY_ORIG="$(stty -g)" # save stty settings
stty echo # enable echo
echo "Type:"
read -e -r input # use readline (backspace will not work otherwise)
echo "Typed: ${input}"
stty "${STTY_ORIG}" # restore stty settings
It actualy works for me.
The same script
martus#makus-pc:/tmp/src$ dpkg -l | grep bash
ii bash 4.4-5 amd64 GNU Bourne Again SHell
martus#makus-pc:/tmp/src$ uname -a
Linux makus-pc 4.9.0-4-amd64 #1 SMP Debian 4.9.65-3+deb9u1 (2017-12-23) x86_64 GNU/Linux
Edit: Does the script works without piping less? Less won't show anything typed untill you hit enter.

Ctrl-C to stop the shell script execution

I have a shellscript as follows. This doesn't terminates on pressing Ctrl-C. Can you guide me on how to modify the following code to kill the execution on Ctrl-C as input.
#!/bin/bash
validateURL()
{
regex='(https?|ftp|file)://[-A-Za-z0-9\+&##/%?=~_|!:,.;]*[-A-Za-z0-9\+&##/%=~_|]'
string=$1
if [[ $string =~ $regex ]]
then
echo "0"
else
echo "1"
fi
}
RED='\033[0;31m'
echo -n "Enter the URL :"
while read URL_REGISTRY
do
if [ $(validateURL $URL_REGISTRY) == "0" ]
then
break
else
echo -e "${RED}Wrong URL entered."
tput sgr0
echo -n "Enter the URL again :"
fi
done
The only way this can happen is if your shell blocks SIGINT. As per your description, your shell seems to do it. Reset the SIGINT in your shell so that your script receives SIGINT.
Run the following in your shell and run the script:
trap - SIGINT
Try:
stty intr "^C"
If that does not work, try:
stty -a
and figure out what is wrong with your settings. If you can't, update your answer with the output of stty -a.
Also make sure that you have not trapped the interrupt signal (2) as mentioned in the other answers.

SIGINT to cancel read in bash script?

I'm writting a bash wrapper to learn some scripting concepts. The idea is to write a script in bash and set it as a user's shell at login.
I made a while loop that reads and evals user's input, and then noticed that, whenever user typed CTRL + C, the script aborted so the user session ends.
To avoid this, I trapped SIGINT, doing nothing in the trap.
Now, the problem is that when you type CTRL + C at half of a command, it doesn't get cancelled as one would do on bash - it just ignores CTRL + C.
So, if I type ping stockoverf^Cping stackoverflow.com, I get ping stockoverfping stackoverflow.com instead of the ping stackoverflow.com that I wanted.
Is there any way to do that?
#!/bin/bash
# let's trap SIGINT (CTRL + C)
trap "" SIGINT
while true
do
read -e -p "$USER - SHIELD: `pwd`> " command
history -s $command
eval $command
done
I know this is old as all heck, but I was struggling to do something like this and came up with this solution. Hopefully it helps someone else out!
#/usr/bin/env bash
# Works ok when it is invoked as a bash script, but not when sourced!
function reset_cursor(){
echo
}
trap reset_cursor INT
while true; do
command=$( if read -e -p "> " line ; then echo "$line"; else echo "quit"; fi )
if [[ "$command" == "quit" ]] ; then
exit
else
history -s $command
eval "$command"
fi
done
trap SIGINT
By throwing the read into a subshell, you ensure that it will get killed with a sigint signal. If you trap that sigint as it percolates up to the parent, you can ignore it there and move onto the next while loop. You don't have to have reset_cursor as its own function but I find it nice in case you want to do more complicated stuff.
I had to add the if statement in the subshell because otherwise it would ignore ctrl+d - but we want it to be able 'log us out' without forcing a user to type exit or quit manually.
You could use a tool like xdotool to send Ctrl-A (begin-of-line) Ctrl-K (delete-to-end-of-line) Return (to cleanup the line)
#!/bin/bash
trap "xdotool key Ctrl+A Ctrl+k Return" SIGINT;
unset command
while [ "$command" != "quit" ] ;do
eval $command
read -e -p "$USER - SHIELD: `pwd`> " command
done
trap SIGINT
The please have a look a bash's manual page, searching for ``debug'' keyword...
man -Pless\ +/debug bash

create read/write environment using named pipes

I am using RedHat EL 4. I am using Bash 3.00.15.
I am writing SystemVerilog and I want to emulate stdin and stdout. I can only use files as the normal stdin and stdout is not supported in the environment. I would like to use named pipes to emulate stdin and stdout.
I understand how to create a to_sv and from_sv file using mkpipe, and how to open them and use them in SystemVerilog.
By using "cat > to_sv" I can output strings to the SystemVerilog simulation. But that also outputs what I'm typing in the shell.
I would like, if possible, a single shell where it acts almost like a UART terminal. Whatever I type goes directly out to "to_sv", and whatever is written to "from_sv" gets printed out.
If I am going about this completely wrong, then by all means suggest the correct way! Thank you so much,
Nachum Kanovsky
Edit: You can output to a named pipe and read from an other one in the same terminal. You can also disable keys to be echoed to the terminal using stty -echo.
mkfifo /tmp/from
mkfifo /tmp/to
stty -echo
cat /tmp/from & cat > /tmp/to
Whit this command everything you write goes to /tmp/to and is not echoed and everything written to /tmp/from will be echoed.
Update: I have found a way to send every chars inputed to the /tmp/to one at a time. Instead of cat > /tmp/to use this command:
while IFS= read -n1 c;
do
if [ -z "$c" ]; then
printf "\n" >> /tmp/to;
fi;
printf "%s" "$c" >> /tmp/to;
done
You probably want to use exec as in:
exec > to_sv
exec < from_sv
See sections 19.1. and 19.2. in the Advanced Bash-Scripting Guide - I/O Redirection
Instead of cat /tmp/from & you may use tail -f /tmp/from & (at least here on Mac OS X 10.6.7 this prevented a deadlock if I echo more than once to /tmp/from).
Based on Lynch's code:
# terminal window 1
(
rm -f /tmp/from /tmp/to
mkfifo /tmp/from
mkfifo /tmp/to
stty -echo
#cat -u /tmp/from &
tail -f /tmp/from &
bgpid=$!
trap "kill -TERM ${bgpid}; stty echo; exit" 1 2 3 13 15
while IFS= read -n1 c;
do
if [ -z "$c" ]; then
printf "\n" >> /tmp/to
fi;
printf "%s" "$c" >> /tmp/to
done
)
# terminal window 2
(
tail -f /tmp/to &
bgpid=$!
trap "kill -TERM ${bgpid}; stty echo; exit" 1 2 3 13 15
wait
)
# terminal window 3
echo "hello from /tmp/from" > /tmp/from

Resources