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.
Related
EDIT: Corrected process/thread terminology
My shell script has a foreground process that reads user input and a background process that prints messages. I would like to print these messages on the line above the input prompt rather than interrupting the input. Here's a canned example:
sleep 5 && echo -e "\nINFO: Helpful Status Update!" &
echo -n "> "
read input
When I execute it and type "input" a bunch of times, I get something like this:
> input input input inp
INFO: Helpful Status Update!
ut input
But I would like to see something like this:
INFO: Helpful Status Update!
> input input input input input
The solution need not be portable (I'm using bash on linux), though I would like to avoid ncurses if possible.
EDIT: According to #Nick, previous lines are inaccessible for historical reasons. However, my situation only requires modifying the current line. Here's a proof of concept:
# Make named pipe
mkfifo pipe
# Spawn background process
while true; do
sleep 2
echo -en "\033[1K\rINFO: Helpful Status Update!\n> `cat pipe`"
done &
# Start foreground user input
echo -n "> "
pid=-1
collected=""
IFS=""
while true; do
read -n 1 c
collected="$collected$c"
# Named pipes block writes, so must do background process
echo -n "$collected" >> pipe &
# Kill last loop's (potentially) still-blocking pipe write
if kill -0 $pid &> /dev/null; then
kill $pid &> /dev/null
fi
pid=$!
done
This produces mostly the correct behavior, but lacks CLI niceties like backspace and arrow navigation. These could be hacked in, but I'm still having trouble believing that a standard approach hasn't already been developed.
The original ANSI codes still work in bash terminal on Linux (and MacOS), so you can use \033[F where \033 is the ESCape character. You can generate this in bash terminal by control-V followed by the ESCape character. You should see ^[ appear. Then type [F. If you test the following script:
echo "original line 1"
echo "^[[Fupdated line 1"
echo "line 2"
echo "line 3"
You should see output:
updated line 1
line 2
line 3
EDIT:
I forgot to add that using this in your script will cause the cursor to return to the beginning of the line, so further input will overwrite what you have typed already. You could use control-R on the keyboard to cause bash to re-type the current line and return the cursor to the end of the line.
I have a bash script that should run as follows: Read a line from a (large) file, process the line, display the results, wait for a run signal from the user, start over. Right now it looks like
while read newline
do
# process newline
# display newline
read go_ahead
done < my_input_file.txt
The processing and displaying look sort of OK, but it's hard to tell because -- here's my problem -- the script won't stop to read the go_ahead variable. I am guessing this is because it thinks it's supposed to read newline and go_ahead from my_input_file.txt? In any event, can someone tell me how to fix this?
The easiest way to do this might be to use a separate file descriptor for your input data. Something like this:
#!/usr/bin/env bash
exec 3< /path/to/inputfile.txt
while read -u 3 newline; do
processed=$(tr '[a-z]' '[A-Z]' <<<"$newline")
printf '%s\n' "$processed"
read go_ahead
done
#!/bin/bash
exec 3>&1
while read line; do
echo $line
read -u 3 -p 'continue(y/n)? ' yn
[[ $yn == n ]] && break
done < "$1"
exit 0
The -u arg to read let's you specify another descriptor.
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).
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
I have a bash script that I mostly use in interactive mode. However, sometimes I pipe in some input to the script. After processing stdin in a loop, I copy a file using "-i" (interactive). However, this never gets executed (in pipe mode) since (I guess) standard input has not been flushed. To simplify with an example:
#!/bin/bash
while read line
do
echo $line
done
# the next line does not execute
cp -i afile bfile
Place this in t.sh, and execute with:
ls | ./t.sh
The read is not executed.
I need to flush stdin before the read. How could it do this?
This has nothing to do with flushing. Your stdin is the output of ls, you've read all of it with your while loop, so read gets EOF immediately. If you want to read something from the terminal, you can try this:
#!/bin/bash
while read line
do
echo $line
done
# the next line does execute
read -p "y/n" x < /dev/tty
echo "got $x"
I'm not sure it's possible to do what you want here (i.e. having the read take its input from the user and not from ls). The problem is that all standard input for your script is taken from the pipe, period. This is the same file descriptor, so it will not 'switch' to the terminal just because you want it to.
One option would be to run the ls as a child of the script, like this:
#!/bin/bash
ls | while read line
do
echo $line
done
read -p "y/n" x
echo "got $x"