How can I pause in zsh? - bash

I want to write this bash loop for zsh
while true; do echo "print something"; read -p "pause"; done
This loop echos, then waits for the user to press enter. If I enter it as is, the read statement doesn't pause, causing zsh to infinitely echo "print something" without waiting for the user to press enter.

In zsh:
read -s -k '?Press any key to continue.'
From man zshbuiltins:
-s Don't echo back characters if reading from the terminal.
-k Read only one character.
name?prompt Name is omitted, thus user input is stored in the REPLY variable (and we ignore it). The first argument contains a ?, thus the remainder of this word is used as a prompt on standard error when the shell is interactive.
To include a newline after the prompt:
read -s -k $'?Press any key to continue.\n'
$'' is explained under QUOTING in man zshmisc.
Finally, a pause function that takes an arbitrary prompt message in a script that does what the OP asks:
#!/usr/bin/env zsh
pause() read -s -k "?$*"$'\n'
while true; do
echo "print something"
pause "pause"
done

Since this is about the only search result I could find, and I found it helpful but still a bit confusing, here is another way of putting it: If all you want to do is echo a line of text and wait for the user to press enter ...
read \?"I am waiting for you to press [Enter] before I continue."

It looks like -p does something different in zsh. You will probably need something like read some_variable\?pause.

#!/bin/zsh
pause()
{
echo "$*"; read -k1 -s
}
now we can call the function with any prompt text:
pause "paused! press any key to continue"
pause "you can write anything here :)"

If you want a way that works in both bash and zsh, and ensures I/O to/from the terminal:
# Prompt for a keypress to continue. Customise prompt with $*
function pause {
>/dev/tty printf '%s' "${*:-Press any key to continue... }"
[[ $ZSH_VERSION ]] && read -krs # Use -u0 to read from STDIN
[[ $BASH_VERSION ]] && </dev/tty read -rsn1
printf '\n'
}
export_function pause

Related

Korn Shell - How can I make “Press any key to continue”

I am writing korn shell script. I need to put funtionality called "Press any key to continue" to execute rest of the script.How can I achieve this?
Thanks in advance
You can use read command:
read -n1 -r -p "Press space to continue..." key
if [ "$key" = '' ]; then
# Space pressed, do something
# echo [$key] is empty when SPACE is pressed # uncomment to trace
else
# Anything else pressed, do whatever else.
# echo [$key] not empty
fi
This is very simple script.
#!/usr/bin/ksh
echo "First Method"
read -s -n 1 -p "Press any key to continue..."
# insert echo here for cleaner output
echo
echo "Second Method"
echo "Press any key to continue..."
read -s -n 1 any_key
echo "Now exiting"
exit 0
Information about the read command:
To get input from the keyboard, you use the read command. The read command takes input from the keyboard and assigns it to a variable. Here is an example:
echo -n "Enter some text > "
read text
echo "You entered: $text"
read is the command for input (usually form terminal). There is (nearly) no way around the <Enter>, its the way to tell read to start processing the input.
If you want to start processing after whatever keystroke, you have to dive deep into terminal settings via stty, changing to raw mode and .....
And it is likely your terminal is in foul mood when you are coming out of that.
The effort to avoid side effects is usually not worth the effect.
Think about, you changed the terminal mode, waiting for a keystroke, do you allow Ctrl-C, backspace, ESC, ... to be keystroke or do they need special handling? You changed the terminal mode, you are respnsible now. How to set the terminal to a sane state if the process has been killed , ....
If you are up for a big challenge, go for it :-)
OK, I understand why my post was deleted. Here I go again.
What I did is to create a function 'readOne' and call it when needed.
readOne () {
tput smso
echo "Press any key to return \c"
tput rmso
oldstty=`stty -g`
stty -icanon -echo min 1 time 0
dd bs=1 count=1 >/dev/null 2>&1
stty "$oldstty"
echo
}
Here is the explanation from the author :
The tty driver controls how input lines that you type are delivered to programs. Normally a tty driver will wait until a complete line is available. It also handles stuff like backspace so that program doesn'y need to. The stty command lets you change the way the tty driver works. "stty -a" will display all of the settings. You should do that to see what's available.
"stty -g" displays all of the settings too. But it's encoded and you can't understand the output. But you can save the output and feed it back into the stty command. So:
oldtty='stty -g'
stty $oldstty
will save and restore the original settings of the tty driver.
stty -icanon -echo min 1 time 0
is setting some options in the tty driver. -icanon turns off all special character processing. Now a backspace will be passed to the program rather than being processed. And a carriage return won't terminate a line. So now min and time control when a read has finished. "min 1" says we need at least one character. "time 0" means that we won't wait a while before completing a read. So each read from the program may return after just one character.
the -echo just turns off echo. I would not have done that.
dd is a program that is prepared to read data that is not organized into lines. This dd will read one block (count=1) of data. And that block will be one character in length (size=1). So the dd will read one character and return to the script.
The final echo moves the cursor to the next line.

Pause for loop until key is pressed

How can I run a for loop which pauses after each iteration until a key is pressed?
for example, if I wanted to print the line number of file1, file2, file3, but only continuing each after pressing a key:
for f in dir/file? ; do wc -l $f ; pause until key is pressed ; done
Apologies if this is trivial, I'm new to the coding.
Use the read command to wait for a single character(-n1) input from the user
read -p "Press key to continue.. " -n1 -s
The options used from the man read page,
-n nchars return after reading NCHARS characters rather than waiting
for a newline, but honor a delimiter if fewer than NCHARS
characters are read before the delimiter
-s do not echo input coming from a terminal
-p prompt output the string PROMPT without a trailing newline before
attempting to read
#Stewart: Try:
cat script.ksh
trap "echo exiting...; exit" SIGHUP SIGINT SIGTERM
for file in /tmp/*
do
wc -l $file
echo "Waiting for key hit.."
read var
if [[ -n $var ]]
then
continue
fi
done
This script will be keep on running until/unless system get a signal to kill it (eg--> cntl+c etc). Let me know if this helps. Logic is simple created a trap first line of script to handle user's interruptions, then created a for loop which will look into a directory with all the files(you could change it accordingly to your need too). then wc -l of the current file as per yours post shown. Then ask user to enter a choice if user enters anything then it will go to loop again and again.

Bash Unix: How can i pause a process without using sleep command

Hi im trying to pause the execution of a process so that a user cannot make multiple entries until 5 seconds have elapsed. I tried using sleep but sleep simply stops and then executes all the inputs the user ran while the process was asleep, i dont want there to be any input read from when the program was sleeping.
example of what i want: a chat bot
input 1: hi
output: "reply from program"
input 2 (before 5 seconds is up): "whats new"
-no output-
input 3 (5 seconds have passed): "how are you"
output: im fine.
example of what sleep command does:
input 1. "hi"
output "hey"
input 2 (before 5 seconds): "whats new"
-no output- waits
input 3: "how are you"
output: "not much is new"
output: "im fine"
You can read and throw away the user's input after the 5 seconds is up
# 1.
read -p "prompt 1: " first_thing
sleep 4
# this reads and ignores whatever the user has typed during the sleep
while read -t 1 _garbage; do :; done
# 2.
read -p "prompt 2: " next_thing
reads -t 1 option is a timeout of one second if there's nothing to read.
Testing
with input
$ read -p "prompt 1: " first_thing; sleep 4; while read -t 1 _garbage; do :;done ; read -p "prompt 2: " next_thing
prompt 1: foo
bar
baz
prompt 2: qux
$ echo $first_thing $next_thing
foo qux
no "extra" input before the 2nd read -- process does not "hang" awaiting input
$ read -p "prompt 1: " first_thing; sleep 4; while read -t 1 _garbage; do :;done ; read -p "prompt 2: " next_thing
prompt 1: hello
prompt 2: world
$ echo $first_thing $next_thing
hello world
This is by no means easy, and you need to be more specific about how you expect the script to respond to keyboard input.
There is no way to "lock" the keyboard; the user can continue punching keys as they see fit. If you are content for the keys to be echoed, but you want the input to be ignored, you could do something like the following:
# Beware! Read the entire answer; don't just use this command
timeout 5 bash -c 'while :;do read -s -d ""; done'
The timeout utility runs a command, killing it when the specified number of seconds have elapsed; the specified number may be a decimal fraction. The loop around the read command is necessary because the read would other terminate as soon as the Enter key is pressed; while :; do is a standard idiom for "loop forever".
The timeout command is part of Gnu coreutils. If you don't have it, perhaps because you are using a BSD derivative, you can probably find alternatives. There is a FreeBSD command, probably available on other BSDs including Mac OS X, called timelimit; I believe the correct invocation would be to replace timeout 5 with timelimit -t 5 -s9, but I don't have any easy way of testing.
You need to get the read command to actually read input immediately, as opposed to waiting until the Enter key is pressed. Otherwise, the typed input will still be available to the next command after the read is terminated.
There are several ways to do this. One is to use the -n 1 flag to cause the read to return after each character; another one is to use -d "" to set the end of input character to NUL, which has the side effect of putting read into character-at-a-time mode.
Also, you will probably want to suppress echo of the keys pressed while you are in the read loop. You can do that by adding the -s flag to the read command, but again that will have the side effect of leaving the terminal in "no echo" mode when the read command is interrupted. [Note 1]
Unfortunately, you'll probably find that the terminal settings have been permanently changed, because when read is killed by the timeout command, it doesn't have a chance to restore the terminal settings. So you'll end up with a terminal which doesn't echo, doesn't handle backspace and other line-editing commands, and doesn't honor Ctrl-D, amongst other issues.
To avoid this problem, you need to save and restore the terminal settings. You can do that with the stty command, as follows:
# Save the terminal settings
saved=$(stty -g)
# Ignore input for 5 seconds, suppressing echo
timeout 5 bash -c 'while :;do read -s -d ""; done'
# Restore the terminal settings
stty "$saved"
If you don't suppress echo, you'll find that your input prompt may appear on the same line as the ignored input. You could avoid that by outputting a "carriage return / erase to end of line" control sequence before the prompt:
tput cr; tput el; read -p "Give me some input: "
The answer of #glenn is a good answer!
Maybe you don't know in front how long you are going to sleep (you are calling
some other functions).
When you just want to flush the input queue, you can try a similar approach:
echo "What is your name?"
read name
echo "Please be quiet for 5 seconds"
sleep 5
smalltime=0.000001
read -t ${smalltime} -s garbage
while [ -n "${garbage}" ]; do
echo "Flushing $garbage"
read -t ${smalltime} -s garbage
done
echo "Hello ${name}, please say something else"
read y
echo "You said $y"

How to use up arrow key in shell script to get previous command

I am trying to write my console with some basic functionalities.
Here is what I am doing.
function help()
{
echo "add(a,b,...)"
}
function add()
{
arg=$(echo $1 | cut -d"(" -f2)
sum=0
for number in `echo ${arg} | sed -e 's/[,) ]\+/\n/g'` ; do
sum=$(($sum + $number))
done
echo $sum
}
while true
do
echo -n "mycon#avi>>"
read command
opt=$(echo "$command" | cut -d"(" -f1)
case $opt in
"exit"|"q")
exit
;;
"help")
help
;;
"add")
add $command
;;
esac
done
I am saving this file as mycon
when I run this script ./mycon
mycon#avi>>add(2,3)
5
mycon#avi>>
Now in this moment when I am pressing up arrow key, I want to get the above add(2,3) command. what is the way to do this ??
Thank You
Bash-only solution:
Change read command to read -e command so that bash will enable the readline library.
Add the command history -s "$command" to include the line read into the history.
Note that read command will delete trailing whitespace from the typed command before assigning the line to command, unless you invoke it with IFS set to the empty string. Also, read will normally treat backslashes as escape characters, which is usually undesirable; you can suppress that behaviour with the -r flag. Finally, you can get read to print the prompt, which will work better with readline, using the -p option. So your final sequence might look like this:
while IFS= read -e -p "mycon#avi>> " command; do
history -s "$command"
# ... process the command
done
(Using the read command as the condition in the while statement causes the while loop to terminate if the user enters an EOF character.)
For more information on read and history, use the built-in bash help command (help read / help history)

How to just input 'y' without having to press ENTER?

Using this portion of a bash script as an example
{
read -p "Do you want to update the tv feed? [y/n/q] " ynq
case $ynq in
[Yy]* ) rm ~/cron/beeb.txt; /usr/bin/get-iplayer --type tv>>~/cron/beeb.txt;;
[Nn]* ) echo;;
[Qq]* ) exit;;
* ) echo "Please answer yes or no. ";;
esac
}
How do I get it so that you can press y and not have to press Enter for it to be accepted please?
Add -n 1 to the read command's options. From the bash manpage:
-n nchars
read returns after reading nchars characters rather than
waiting for a complete line of input.
BTW, you should also double-quote "$ynq" -- sometimes users will just press return, which can cause weird behavior if the variable isn't double-quoted. Also, note that read -n is a bash extension, so make sure you're using bash (i.e. #!/bin/bash or similar for the first line of the script), not a brand-x shell (#!/bin/sh or similar).
Use -n1 with read to specify max number of input length to 1:
read -n1 -p "Do you want to update the tv feed? [y/n/q] " ynq
I am on Mac and using read -n1 $user_decision doesn't do the trick for some reason in bash, sh, or zsh. So, I am using this which works across all:
#!/bin/zsh
# -k1 = First char pressed without waiting, for /r or /n.
# -t3 = timeout for 3 seconds
# -s = prevent outputting the input back to stdout.
echo "Press any letter..."
read -t3 -k1 -s user_decision
# Prints 1st arbitrary keypress entered during 3s timeout
echo $user_decision # EG: "y", or "n" for instance.
To simplify, you can just use read -k1 user_decision to get precisely what you requested set into the value for the variable name $user_decision, without waiting for /r or /n (hitting enter or return).

Resources