I recently created a different thread with an issue concerning a for loop in a bash script I was writing for my GCSE coursework. I have another issue with the same bash script (however it has evolved a fair bit since last time).
Here is the code:
#!/bin/bash
# A script that creates users.
uerror='^[0-9]+$'
echo "This is a script to create new users on this system."
echo "How many users do you want to add? (in integer numbers)"
read am
echo " "
if [[ $am =~ $uerror ]] ; then
echo "ERROR: Please use integer numbers."
echo "Please re-enter the amount."
read am ;
else
echo " "
for i in $(seq "$am")
do
echo "Enter a username below:"
read usern
sudo useradd $usern
sudo passwd $usern
echo " "
echo "User $i '$usern' added."
echo " "
echo "What group do you want to add $usern to?"
read group
sudo usermod $usern -aG $group
echo "$usern added to $group"
echo " "
echo "-------------------"
echo " "
done
fi
The issue is in the if statement. It's purpose is to stop users entering anything other than an integer number. But for some reason, I don't seem to be able to capture the input from the read am part. Instead the script skips straight onto the for loop where the $(seq "$am") obviously will have issues comprehending an input that is not a number.
The output from this error is as follows.
seq: invalid floating point argument
However, I don't think this is relevant because as far as I can tell, the issue is with the if / else statement.
If anyone could point me in the right direction of what I need to do to fix this, I would be greatly appreciative.
I'd also like to iterate that I am still learning how to write bash scripts (and not in a particularly organised manner) so I've probably made a very simple mistake. Apologies for that.
Thanks,
Callum.
EDIT: I mistyped an echo message, I've now changed that so it actually makes sense.
If you want to read in a number and make sure it is a number use a while loop:
while read -p 'type a number:' n ; do
# Exit the loop if the input is a number
[[ "$n" =~ ^[0-9]+$ ]] && break
echo "This was not a number! Don't trick me!"
done
# Now can use `seq`
seq "$n"
The if statement in your example would do the completely the wrong thing. It checks if the input is a number and in that case asks for the input again and exits the script. If you don't type a number, it uses the (wrong) input in the else branch.
Replace your whole file with this:
#!/bin/bash
# A script that creates users.
uerror='^[0-9]+$'
echo "This is a script to create new users on this system."
echo "How many users do you want to add? (in integer numbers)"
read am
echo " "
while true; do
if [[ $am =~ $uerror ]] ; then
break;
else
echo "Must be integer"
echo "Please re-enter: "
read am ;
fi
done
for i in $(seq "$am")
do
echo "Enter a username below:"
read usern
sudo useradd $usern
sudo passwd $usern
echo " "
echo "User $i '$usern' added."
echo " "
echo "What group do you want to add $usern to?"
read group
sudo usermod $usern -aG $group
echo "$usern added to $group"
echo " "
echo "-------------------"
echo " "
done
Related
I'm trying to get this script to loop back and restart if the user inputs a response that does not match the choices allowed but then enters a correct response the second time around. I tried using continue, but it loops infinitely. Any thoughts?
`
#!/bin/bash
#Obtaing user selection and input
echo " Gathering list of users on this machine..."
sleep 2
echo "$( ls /Users )"
echo "From the list above, which user did you want to work with?"
read userSelection
echo "What is that user's password?"
#Hiding User's Password
stty -echo
read userSelectionPassword
stty echo
echo "Did you want [enable], [disable], or check the current [status] of Secure Token for $userSelection?"
read taskSelection
#Converting input to lowercase
taskSelectionLower=$(echo $taskSelection | tr '[:upper:]' '[:lower:]')
#Running commands
while [ true ]
do
if [[ $taskSelectionLower == "enable" ]]; then
echo "Enabling..."
echo "$(sysadminctl -adminUser AdminUser -adminPassword AdminPass -secureTokenOn $userSelection -password $userSelectionPassword)"
break
elif [[ $taskSelectionLower == "status" ]]; then
echo "Displaying $userSelection current Secure Token status..."
echo "$( sysadminctl -secureTokenStatus $userSelection )"
break
elif [[ $taskSelectionLower == "disable" ]]; then
echo "Disabling..."
echo "$(sysadminctl -adminUser AdminUser -adminPassword AdminPass -secureTokenOff $userSelection -password $userSelectionPassword)"
break
else
echo "Incorrect selection made..."
echo "Did you want [enable], [disable], or check the current [status] of Secure Token for $userSelection?"
read taskSelection
exit
fi
done
`
Attempted using continue at the end of the condition, but loops infinitely.
Expected outcome would be for the for loop to restart, allowing the user to input a correct response and get the correct output.
The select command was designed exactly for this sort of interactive script.
Some hopefully useful observations -
echo " Gathering list of users on this machine..."
sleep 2 # why?
This sleep seems to serve no purpose but to annoy the user.
echo "$( ls /Users )" # why?? Don't do this.
This is exactly the same as
ls /Users # same output. Keep it simple.
Also, since you are bash instead of sh, rather than
taskSelectionLower=$(echo $taskSelection | tr '[:upper:]' '[:lower:]')
try declaring the variable as lowercase -
declare -l taskSelectionLower
read taskSelectionLower
or handle it with parameter parsing syntax -
read taskSelectionLower
taskSelectionLower="${taskSelectionLower,,}"
but why let them possibly mistype at all?
Consider using a select statement.
Rather than building your own loop, presenting options, worrying about formatting, etc, let tools that already do that handle it for you.
My rewrite using echo to show the commands to be executed - take those out and it should run fine.
#!/bin/bash
cd /c/Users
echo "Please select a valid user from this system."
select u in */; do
if [[ -d "$u" ]]
then userSelection="${u%/}"; break
else echo "Please enter a number from the presented options."
fi
done # get just the username
cd $OLDPWD # if needed
admSet() { # no reason to ask for the password just to get status...
read -sp "what's $userSelection's password? " userSelectionPassword
echo # reading the password leaves the cursor on the previous line
echo sysadminctl -adminUser AdminUser -adminPassword AdminPass \
-secureToken$1 "$userSelection" -password "$userSelectionPassword"
}
select taskSelection in enable disable status; do # case is controlled
case "$taskSelection" in
status) echo "Displaying $userSelection current Secure Token status..."
echo sysadminctl -secureTokenStatus $userSelection ;;
enable) echo "Enabling..." ; admSet On ;;
disable) echo "Disabling..." ; admSet Off ;;
*) echo "Please enter a number from the presented options."
continue ;;
esac
break
done
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
I am trying to make a shell script that works as an ordering system. I have started with the first steps but it does not take the input I am not sure of what I am doing wrong. I have attached an image of what end result should be. What is the next step I should take and what should I begin to research
#!/bin/bash
clear
echo "orderBeds"
read -p "Please Enter you choice (Quit/Order)" order
if [$order -e Order|order]
then
echo "Please enter you name?"
elif [$order -e Quit|quit]
then
exit
fi
done
I'll start giving some general advice.
1) [ is a command. That means you probably don't want to expand a variable just next to it without separating them with white spaces.
2) If you will check against more than one option, use the case construct. Apart from giving you the chance of a better structure, you'll be able to use globbing expressions as options to match against.
That said, let's rewrite your code:
#! /bin/bash
clear
echo "orderBeds"
read -p "Please Enter your choice (Quit/Order)" order
case "$order" in
[Oo]rder)
read -p "Please enter your name: " name
echo "$name placed an order."
break
;;
[Qq]uit)
exit
;;
esac
the -e flag is for numerical equivalency.
Here is a corrected bash script to get you started:
#!/bin/bash
clear
echo "orderBeds"
read -p "Please enter your choice (Quit/Order) " order
if [ $order == "order" ] || [ $order == "Order" ]
then
read -p "Please enter your name " name
echo "$name placed an order"
elif [ $order == "quit"] || [ $order == "Quit" ]
then
exit
fi
Note the use of == instead of -e and the separation of the or clauses.
When I run this script the first question it asks is "Would you like to use curl? [Y/N]:" to which my reply is often Y or y. However, when I do that the instant output I get is "Unknown!". I'm expecting to see the next question from the code which is "Great, do you want to ignore certificates [Y/N]: "
Can anyone edit my code to make it work as expected? And tell me why?
#!/bin/bash
echo "Build command"
read -r -e -p "Would you like to use curl? [Y/N]: "
curlstring="curl"
if [[ "${input,,}" == "y" ]]; then
read -r -e -p "Great, do you want to ignore certificates [Y/N]: " input
if [[ "$input" == "y" ]]; then
curlstring=$curlstring" -k"
else
curlstring=$curlstring" "
fi
echo "$curlstring"
elif [[ "${input,,}" == "n" ]]; then
echo "Bye"
else
echo "Unknown!"
exit 0
fi
The error is your are not actually capturing the user input from the first read command, change it,
read -r -e -p "Would you like to use curl? [Y/N]: " input
Also bash is quite sensitive about the constructs used, the line number 15
fi echo "$curlstring"
will likely throw a syntax error saying below when you give a Y or a y as one of the options,
Would you like to use curl? [Y/N]: Y
script.sh: line 15: syntax error near unexpected token `echo'
script.sh: line 15: ` fi echo "$curlstring" '
change it to separate lines.
So somebody showed me how to use condition(s) to test if a user had typed input for a password.
I wanna take a their example a step further and use a loop (at least thats what I think it's call).
Here is their example:
read -s -p "Enter new password: " NEWPASS
if test "$NEWPASS" = ""; then
echo "Password CAN NOT be blank re-run sshd_config"
exit 1;
fi
Instead of exiting the script I want it to keep asking for the input until there is some.
I wanna make a statement like this but I was doing it wrong:
(Now using top example)
The user is given a chance to enter new password and fill the variable value.
read -s -p "Enter new password:" NEWPASS
echo ""
The next portion checks the variable to see if it contains a value, while the value is null it request the user to fill the value indefinitely. ( a loop)
while [[ -z "$NEWPASS" ]]; do
echo ""
echo "Password CAN NOT be blank"
echo ""
read -s -p "Enter new password:" NEWPASS;
echo ""
done
This line searches a file containing a set of variables used by another file. It then searches the file for a line containing PASS=.* (.* meaning anything) then writes PASS=$NEWPASS ($NEWPASS being variable)
sed -i -e"s/^PASS=.*/PASS=$NEWPASS/" /etc/sshd.conf
Thank you for all the help, I'm going to use this and learn from it.
This while loop should work:
while [[ -z "$NEWPASS" ]]
do
read -s -p "Enter new password: " NEWPASS
done
while read -s -p 'Enter new password: ' NEWPASS && [[ -z "$NEWPASS" ]] ; do
echo "No-no, please, no blank passwords!"
done