Shell Script, read on same line after echoing a message - linux

Following the shell script that I am executing
#!/bin/sh
echo "Enter [y/n] : "
read opt
Its output is
Enter [y/n] :
Y
I want that the variable should be read on the same line like below
Enter [y/n] : Y
Should be simple I guess, but I am new to bash scripting.

Solution: read -p "Enter [y/n] : " opt
From help read:
-p prompt output the string PROMPT without a trailing newline before
attempting to read

The shebang #!/bin/sh means you're writing code for either the historical Bourne shell (still found on some systems like Solaris I think), or more likely, the standard shell language as defined by POSIX. This means that read -p and echo -n are both unreliable.
The standard/portable solution is:
printf 'Enter [y/n] : '
read -r opt
(The -r prevents the special treatment of \, since read normally accepts that as a line-continuation when it's at the end of a line.)
If you know that your script will be run on systems that have Bash, you can change the shebang to #!/bin/bash (or #!/usr/bin/env bash) and use all the fancy Bash features. (Many systems have /bin/sh symlinked to bash so it works either way, but relying on that is bad practice, and bash actually disables some of its own features when executed under the name sh.)

echo -n "Enter [y/n] : " ; read opt
OR! (Later is better)
read -p "[y/n]: " opt

use -n handle in echo, that will avoid trailing newline
echo -n "Enter [y/n] : "
read opt

Related

How do -s and -p alter the read command?

I'm trying to interpret this block of code. Searched google to see what these commands mean and no luck. I put my interpretation of what each line/block means to me. If I am wrong, please correct me. I am new to unix commands. Code:
#!/bin/bash
# input 1st command line argument for the version.
export VERSION=$1
# if user didn't input a version, print the echo message and exit (not sure what -n means but I am assuming)
if [[ ! -n "$VERSION" ]]; then
echo "Missing Version"
exit 1
fi
# creating variable UNAME that tells who the person is (their name)
export UNAME='whoami'
# no idea what -s and -p mean but i think this prints the message "enter password for $UNAME" and stores it in a new variable named PASSWORD. the $UNAME will print whatever whoami said.
read -s -p "Enter password for $UNAME: " PASSWORD
echo ""
The -p flag issues a prompt before reading input into a variable
The -s flag stop the typed response from being shown (i.e. for a sensitive password)
More information is available here:
https://linuxhint.com/bash_read_command/
-p
prompt output the string PROMPT without a trailing newline before
attempting to read.
-s
do not echo input coming from a terminal.

bash script loop breaks [duplicate]

I have the following shell script. The purpose is to loop thru each line of the target file (whose path is the input parameter to the script) and do work against each line. Now, it seems only work with the very first line in the target file and stops after that line got processed. Is there anything wrong with my script?
#!/bin/bash
# SCRIPT: do.sh
# PURPOSE: loop thru the targets
FILENAME=$1
count=0
echo "proceed with $FILENAME"
while read LINE; do
let count++
echo "$count $LINE"
sh ./do_work.sh $LINE
done < $FILENAME
echo "\ntotal $count targets"
In do_work.sh, I run a couple of ssh commands.
The problem is that do_work.sh runs ssh commands and by default ssh reads from stdin which is your input file. As a result, you only see the first line processed, because the command consumes the rest of the file and your while loop terminates.
This happens not just for ssh, but for any command that reads stdin, including mplayer, ffmpeg, HandBrakeCLI, httpie, brew install, and more.
To prevent this, pass the -n option to your ssh command to make it read from /dev/null instead of stdin. Other commands have similar flags, or you can universally use < /dev/null.
A very simple and robust workaround is to change the file descriptor from which the read command receives input.
This is accomplished by two modifications: the -u argument to read, and the redirection operator for < $FILENAME.
In BASH, the default file descriptor values (i.e. values for -u in read) are:
0 = stdin
1 = stdout
2 = stderr
So just choose some other unused file descriptor, like 9 just for fun.
Thus, the following would be the workaround:
while read -u 9 LINE; do
let count++
echo "$count $LINE"
sh ./do_work.sh $LINE
done 9< $FILENAME
Notice the two modifications:
read becomes read -u 9
< $FILENAME becomes 9< $FILENAME
As a best practice, I do this for all while loops I write in BASH.
If you have nested loops using read, use a different file descriptor for each one (9,8,7,...).
More generally, a workaround which isn't specific to ssh is to redirect standard input for any command which might otherwise consume the while loop's input.
while read -r line; do
((count++))
echo "$count $line"
sh ./do_work.sh "$line" </dev/null
done < "$filename"
The addition of </dev/null is the crucial point here, though the corrected quoting is also somewhat important for robustness; see also When to wrap quotes around a shell variable?. You will want to use read -r unless you specifically require the slightly odd legacy behavior you get for backslashes in the input without -r. Finally, avoid upper case for your private variables.
Another workaround of sorts which is somewhat specific to ssh is to make sure any ssh command has its standard input tied up, e.g. by changing
ssh otherhost some commands here
to instead read the commands from a here document, which conveniently (for this particular scenario) ties up the standard input of ssh for the commands:
ssh otherhost <<'____HERE'
some commands here
____HERE
ssh -n option prevents checking the exit status of ssh when using HEREdoc while piping output to another program.
So use of /dev/null as stdin is preferred.
#!/bin/bash
while read ONELINE ; do
ssh ubuntu#host_xyz </dev/null <<EOF 2>&1 | filter_pgm
echo "Hi, $ONELINE. You come here often?"
process_response_pgm
EOF
if [ ${PIPESTATUS[0]} -ne 0 ] ; then
echo "aborting loop"
exit ${PIPESTATUS[0]}
fi
done << input_list.txt
This was happening to me because I had set -e and a grep in a loop was returning with no output (which gives a non-zero error code).

'read -r' doesn't read beyond first line in a loop that does ssh [duplicate]

I have the following shell script. The purpose is to loop thru each line of the target file (whose path is the input parameter to the script) and do work against each line. Now, it seems only work with the very first line in the target file and stops after that line got processed. Is there anything wrong with my script?
#!/bin/bash
# SCRIPT: do.sh
# PURPOSE: loop thru the targets
FILENAME=$1
count=0
echo "proceed with $FILENAME"
while read LINE; do
let count++
echo "$count $LINE"
sh ./do_work.sh $LINE
done < $FILENAME
echo "\ntotal $count targets"
In do_work.sh, I run a couple of ssh commands.
The problem is that do_work.sh runs ssh commands and by default ssh reads from stdin which is your input file. As a result, you only see the first line processed, because the command consumes the rest of the file and your while loop terminates.
This happens not just for ssh, but for any command that reads stdin, including mplayer, ffmpeg, HandBrakeCLI, httpie, brew install, and more.
To prevent this, pass the -n option to your ssh command to make it read from /dev/null instead of stdin. Other commands have similar flags, or you can universally use < /dev/null.
A very simple and robust workaround is to change the file descriptor from which the read command receives input.
This is accomplished by two modifications: the -u argument to read, and the redirection operator for < $FILENAME.
In BASH, the default file descriptor values (i.e. values for -u in read) are:
0 = stdin
1 = stdout
2 = stderr
So just choose some other unused file descriptor, like 9 just for fun.
Thus, the following would be the workaround:
while read -u 9 LINE; do
let count++
echo "$count $LINE"
sh ./do_work.sh $LINE
done 9< $FILENAME
Notice the two modifications:
read becomes read -u 9
< $FILENAME becomes 9< $FILENAME
As a best practice, I do this for all while loops I write in BASH.
If you have nested loops using read, use a different file descriptor for each one (9,8,7,...).
More generally, a workaround which isn't specific to ssh is to redirect standard input for any command which might otherwise consume the while loop's input.
while read -r line; do
((count++))
echo "$count $line"
sh ./do_work.sh "$line" </dev/null
done < "$filename"
The addition of </dev/null is the crucial point here, though the corrected quoting is also somewhat important for robustness; see also When to wrap quotes around a shell variable?. You will want to use read -r unless you specifically require the slightly odd legacy behavior you get for backslashes in the input without -r. Finally, avoid upper case for your private variables.
Another workaround of sorts which is somewhat specific to ssh is to make sure any ssh command has its standard input tied up, e.g. by changing
ssh otherhost some commands here
to instead read the commands from a here document, which conveniently (for this particular scenario) ties up the standard input of ssh for the commands:
ssh otherhost <<'____HERE'
some commands here
____HERE
ssh -n option prevents checking the exit status of ssh when using HEREdoc while piping output to another program.
So use of /dev/null as stdin is preferred.
#!/bin/bash
while read ONELINE ; do
ssh ubuntu#host_xyz </dev/null <<EOF 2>&1 | filter_pgm
echo "Hi, $ONELINE. You come here often?"
process_response_pgm
EOF
if [ ${PIPESTATUS[0]} -ne 0 ] ; then
echo "aborting loop"
exit ${PIPESTATUS[0]}
fi
done << input_list.txt
This was happening to me because I had set -e and a grep in a loop was returning with no output (which gives a non-zero error code).

Linux shell script "read" command

So, I'm new to scripting, and I'm having some problems. The command I need to execute is:
read -p Enter_the_DEVICE_Bssid "device1" ;
read -p Enter_the_DEVICE_Bssid "device2" ;
read -p Enter_the_DEVICE_Bssid "device3"
That command works, but when I set it as a variable ie:
com="read -p Enter_the_DEVICE_Bssid "device1" ;
read -p Enter_the_DEVICE_Bssid "device2" ;
read -p Enter_the_DEVICE_Bssid "device3""
and execute it as: $com it does not work. Probably because the read command is trying to set my input to the variables device1 and ; .
Any ideas on how to fix it?
You're running into problems with the order in which things are expanded by the shell.
A simpler example:
$ command='echo one ; echo two'
$ $command
one ; echo two
The semicolon in the value of $command is taken as part of the argument to echo, not as a delimiter between two echo commands.
There might be a way to resolve this so it works the way you want, but why bother? Just define a shell function. Using my simple example:
$ command() { echo one ; echo two ; }
$ command
one
two
$
Or using yours:
com() {
read -p "Enter_the_DEVICE_Bssid: " device1
read -p "Enter_the_DEVICE_Bssid: " device2
read -p "Enter_the_DEVICE_Bssid: " device3
}
Note that I've added ": " at the end of the prompts. I've also removed the unnecessary semicolons and the quotation marks around the variable names (since the argument has to be a valid variable name, it doesn't need to be quoted).
You are not completing the quotes.
com="read -p Enter_the_DEVICE_Bssid "device1"
Quotes always look for a pair and you are missing that.
> com="read -p Enter_the_DEVICE_Bssid: device1"
> $com
Enter_the_DEVICE_Bssid:abc123
> echo $device1
abc123
Here I am using bash shell.

read -p returns "read: no query process" using korn shell ksh

created a simple shell file that contains this:
read -p ThePrompt TheSomthing
echo $TheSomething
Run it, and it returns
-ksh[1]: read: no query process
I've tried single quotes, double quotes around ThePrompt and the man page specifically says "-p" is to use a prompt but it is not working for me. Can anyone tell me what I'm doing wrong? Thanks!
In Ksh you can use this format:
echo "ThePrompt\c"
read TheSomthing
echo $TheSomething
From the googled man page:
The -un and -p options cause input to be read from file descriptor n or the current co-process (see Co-Processes above for comments on this), respectively. If the -s option is used, input is saved to the history file.
To use a prompt, write this instead:
read TheSomething?'ThePrompt'
I found a word around:
echo -n 'prompt: '
read input1
echo -n 'prompt: '
read input2
.
.
.
I don't know why the -p doesn't work as described in the man page. If anyone out there has insights, please reply.
Thanks!
Sorry for reviving this question, but I do my shell scripts in KSH, so I was in the same predicament, until I came with this.
My solution to capture a single character:
$> echo -e "My prompt: \c" ; read -n 1 -s -r FOO ; echo -e "\b"
My prompt:
$> echo $FOO
d
$>
For a longer string remove the "-n 1" from the read command:
$> echo -e "My prompt: \c" ; read -s -r FOO ; echo -e "\b"
My prompt:
$> echo $FOO
this is my entry!!!
$>
I hope this is what you were looking for... Cheers!

Resources