`read` command returns immediately instead of waiting for input - shell

I'm trying to make a simple zsh widget that asks user for a string and sets it as the current command prompt afterwards
zle -N replace-command-buffer
bindkey '\eg' replace-command-buffer
replace-command-buffer() {
local input
echo "Enter a string: "
read -r input
BUFFER="$input"
zle reset-prompt
}
But read command returns immediately without waiting for input. How do I fix that?

Functions executed by zle have their standard input redirected from /dev/null, so your shell's standard input isn't available.
What you probably want is to execute the recursive-edit widget, which will set BUFFER itself.
replace-command-buffer () {
printf "Enter a string: "
zle recursive-edit
zle reset-prompt
}

Related

why is a bash read prompt not appearing when a script is run?

I have a bash script that prompts the user for different information based on what they're trying to do. The prompts are usually done with read -p. Usually it works just fine, the user sees what is being asked, enters what they need to enter, and everything does what it needs to do.
See the following (sanitized) snippet of a function in the script:
#!/bin/bash
function_name() {
if [ "$this_value" == "default" ];then
echo "Value set to default."
read -p "Enter desired value here: " desired_value
desired_value=${desired_value^^}
if [ "${#desired_value}" != 3 ] ;then
echo "$desired_value is an invalid entry."
exit 1
fi
if [ "$desired_value" != "$(some command that returns something to compare against)" ];then
echo "$desired_value is an invalid entry."
exit 1
fi
read -p "You entered $desired_value. Is this correct? [y/N] " reply
reply=${reply,,}
case "$reply" in
y|yes)
$some command that does what I want it to do
;;
*)
echo "User did not enter yes"
exit 1
;;
esac
fi
}
Usually the Enter desired value here and is this correct? lines appear just fine. But in a few instances I've seen, for some reason the read prompt is just blank. A user will see the following:
./script.bash
##unrelated script stuff
##unrelated script stuff
Value set to default.
user_entered_value_here
User did not enter yes. Exiting.
This is a real example that just happened that finally made me come here to ask what is going on (and I modified appropriately to make it an SO post).
What's happening is these two blank lines appear instead of the read -p text. For the first one, the user entered user_entered_value_here because they already know what is supposed to be entered there even without the read prompt. The second one, the Y/N prompt, they don't know, so they see it apparently hanging, and hit Enter instead of y, causing it to trigger the * case option.
I don't understand why the read -p text is not appearing, and especially why it's appearing for most users but not all users. I suspect there's some kind of environmental setting that causes this, but for the life of me I can't figure out what. This is being run only on RHEL 6.2, under bash 4.1.2.
I looked at the man of bash to catch some kind of detail about the read built-in. It is specified that -p option displays the "prompt on standard error, without a trailing newline, before attempting to read any input. The prompt is displayed only if input is coming from a terminal".
Let's consider the simple script input.sh:
#!/bin/bash
read -p "Prompt : " value
echo The user entered: "$value"
Example of execution:
$ ./input.sh
Prompt : foo
The user entered: foo
If stderr is redirected:
$ ./input.sh 2>/dev/null
foo
The user entered: foo
If the input is a pipe
$ echo foo | ./input.sh
The user entered: foo
If the input is a heredoc
$ ./input.sh <<EOF
> foo
> EOF
The user entered: foo
Rewrote your script with shell agnostic grammar and fixed some errors like comparing the string length with a string comparator != = rather than a numerical comparator -ne -eq:
#!/usr/bin/env sh
this_value=default
toupper() {
echo "$1" | tr '[:lower:]' '[:upper:]'
}
function_name() {
if [ "$this_value" = "default" ]; then
echo "Value set to default."
printf "Enter desired value here: "
read -r desired_value
desired_value=$(toupper "$desired_value")
if [ "${#desired_value}" -ne 3 ]; then
printf '%s is an invalid entry.\n' "$desired_value"
exit 1
fi
if [ "$desired_value" != "$(
echo ABC
: some command that returns something to compare against
)" ]; then
echo "$desired_value is an invalid entry."
exit 1
fi
printf 'You entered %s. Is this correct? [y/N] ' "$desired_value"
read -r reply
reply=$(toupper "$reply")
case $reply in
'y' | 'yes')
: "Some command that does what I want it to do"
;;
*)
echo "User did not enter yes"
exit 1
;;
esac
fi
}
function_name

Running function with read command in bash

I need small help with shell function.
I created small function with read command inside, I need to call this function and the return value to outside variable
Check()
{
echo "type something : "
read anyword
echo $anyword
}
out=`Check`
echo $out
the problem is that echo line is not presenting anything until i press enter.
I want that this function will act like python.
Thanks,
The problem is the backticks. If you CTRL+C before the prompt ends, and try to echo $out, you notice that your prompt is saved into the $out variable. Move the prompt outside the function call. Maybe this will help:
Check()
{
read anyword
echo $anyword
}
echo "type something : "
out=`Check`
echo $out
If you're using bash specifically, consider read -p also.
Alternatively, if you want to keep the prompt inside the function:
Check()
{
echo "Type something: " >&2
read anyword
echo $anyword
}
That way, it will write to stderr instead of stdout, so it won't get eaten up.

How do I conditionally redirect output to a file, depending on whether some script argument exists?

I have a script which I would like to use to process some data and input it into a chosen file.
(do some stuff)>$(hostname)_$1
This works fine if my argument is a file name, but what should I pass (or how should I change the script) if I want to output to the terminal, i.e. stdout?
This answer assumes that the question is:
How do I conditionally redirect output to a file, depending on whether some script argument exists?
In other words, how to reduce duplication in a bash function like this:
do_it() {
if [[ -z $1 ]]; then
some very
complicated
commands
else
{ some very
complicated
commands
} > "$(logdir)/$1"
fi
}
If that were the question, a simple solution is to conditionally redirect stdout inside a subshell:
do_it() {
(
if [[ -n $1 ]]; then exec > "$(logdir)/$1"; fi
some very
complicated
commands
)
}
The subshell (created with the parentheses) is necessary in order to limit the effect of the exec command to the scope of the function; otherwise, the redirect would continue when the function returned.
This works well for me. If DUMP_FILE is empty things go to stdout otherwise to the file. It does the job without using explicit redirection, but just uses pipes and existing applications.
function stdout_or_file
{
local DUMP_FILE=${1:-}
if [ -z "${DUMP_FILE}" ]; then
cat
else
sed -n "w ${DUMP_FILE}"
fi
}
function foo()
{
local MSG=$1
echo "info: ${MSG}"
}
foo "bar" | stdout_or_file ${DUMP_FILE}
Of course, you can squeeze this also in one line
foo "bar" | if [ -z "${DUMP_FILE}" ]; then cat; else sed -n "w ${DUMP_FILE}"; fi
Besides sed -n "w ${DUMP_FILE}" another command that does the same is dd status=none of=${DUMP_FILE}

Bash input without pausing the script?

In Bash the only way to get a (user) input seems to be to use the read method, which pauses the rest of the script. Is there any way to receive a command line input (ending with the enter key) without pausing the script. From what I've seen there may be a way to do it with $1 ..?
read -t0 can be used to probe for input if your process is structured as a loop
#!/bin/bash
a='\|/-'
spin()
{
sleep 0.3
a="${a:1}${a:0:1}"
echo -n $'\e'7$'\r'"${a:1:1}"$'\e'8
}
echo 'try these /|\- , dbpq , |)>)|(<( , =>-<'
echo -n " enter a pattern to spin:"
while true
do
spin
if read -t0
then
read a
echo -n " using $a enter a new pattern:"
fi
done
else you could run one command in the background while promptiong for input in the foreground. etc...

Echo output to terminal within function in BASH

I am writing a script in BASH. I have a function within the script that I want to provide progress feedback to the user. Only problem is that the echo command does not print to the terminal. Instead all echos are concatenated together and returned at the end.
Considering the following simplified code how do I get the first echo to print in the users terminal and have the second echo as the return value?
function test_function {
echo "Echo value to terminal"
echo "return value"
}
return_val=$(test_function)
Yet a solution other than sending to STDERR (it may be preferred if your STDERR has other uses, or possibly be redirected by the caller)
This solution direct prints to the terminal tty:
function test_function {
echo "Echo value to terminal" > /dev/tty
echo "return value"
}
-- update --
If your system support the tty command, you could obtain your tty device from the tty command, and thus you may:
echo "this prints to the terminal" > `tty`
send terminal output to stderr:
function test_function {
echo "Echo value to terminal" >&2
echo "return value"
}
Dont use command substitution to obtain the return value from the function
The return value is always available at the $? variable. You can use the variable rather than using command substitution
Test
$ function test_function {
> return_val=10;
> echo "Echo value to terminal $return_val";
> return $return_val;
> }
$ test_function
Echo value to terminal 10
$ return_value=$?
$ echo $return_value
10
If you don't know in which terminal/device you are:
function print_to_terminal(){
echo "Value" >$(tty)
}

Resources