I am having an issue counting the arguments I enter in my shell script. My script is suppose to echo "You have entered too many arguments" by using and Else statement at the end of my code. If I enter more than 3 I get nothing. Am I missing something simple or should I change this to a case statement. I am new to shell scripting so any help would be greatly appreciated.
#!/bin/bash
clear
Today=$(date +"%m-%d-%y")
echo -en '\n'
echo "To find out more about this calculator please refer to the calc.readme file."
echo -en '\n'
echo "You entered $*"
echo -en '\n'
if [ "$#" -eq "3" ]; then
if [ "$2" == "+" ]; then
answer=`expr $1 + $3`
echo "You entered the correct amount of arguments"
echo -en '\n'
echo "Your total is: "`expr $1 + $3`
echo "[$Today]: $# = $answer" >> calc.history
elif [ "$2" == "-" ]; then
answer=`expr $1 - $3`
echo "You entered the correct amount of arguments"
echo -en '\n'
echo "Your total is: "`expr $1 - $3`
echo "[$Today]: $# = $answer" >> calc.history
elif [ "$2" == "*" ]; then
answer=`expr $1 \* $3`
echo "You entered the correct amount of arguments"
echo -en '\n'
echo "Your total is: "`expr $1 \* $3`
echo "[$Today]: $# = $answer" >> calc.history
elif [ "$2" == "/" ]; then
asnwer=`expr $1 / $3`
echo "You entered the correct amount of arguments"
echo -en '\n'
echo "Your total is: "`expr $1 / $3`
echo "[$Today]: $# = $answer" >> calc.history
else
echo -en '\n'
echo "You entered too many arguments."
fi
fi
Your if statements are wrongly nested. You wrote:
if <test on number of arguments>
if <values>
else
<wrong number of arguments>
fi
fi
while you should have written:
if <test on number of arguments>
if <values>
fi
else
<wrong number of arguments>
fi
Your else statement is associated with the wrong if statement, but you can replace the big if-elif chain with a single case statement.
#!/bin/bash
clear
today=$(date +"%m-%d-%y")
echo
echo "To find out more about this calculator please refer to the calc.readme file."
echo
echo "You entered $*"
echo
if [ "$#" -eq "3" ]; then
echo "You entered the correct amount of arguments"
case $2 in
[+-*/])
echo
answer=$(( $1 $2 $3 ))
echo "Your total is $answer"
echo "[$today]: $# = $answer" >> calc.history
;;
*) echo "Unrecognized operator $2"
;;
esac
else
echo
echo "You entered too many arguments."
fi
Related
I wanna write a script which check variables of this script.
I have tried some, but it isn't working. The idea is:
If on of the parameters is a number, print that it is number
If on of the parameters is a character, print that it is character
If 'man parameter' is executable, print that it is might be a function
Script I have tried:
#!/bin/bash
echo Hello $LOGNAME'!'
test $# -eq 0 && echo 'Try again, no parameters were entered' || echo 'Num of parameters is '$#
re='^[0-9]+$'
for i in $*
do
if ![["$i" =~ $re]];then
echo 'Parameter '$i' is alphabetical'
else
if [["$i" =~ $re]];then
echo 'Parameter '$i' is digital'
else
if [ $i];then
echo $i' might be a function. Try to use man of --help'
fi
fi
fi
done
#!/bin/bash
echo "Hello ${LOGNAME}!"
[ "$#" -eq 0 ] && { echo 'Try again, no parameters were entered'; exit 1; }
echo 'Num of parameters is '$#
re='^[0-9]+$'
for i in "$#"
do
if ! [[ "$i" =~ $re ]];then
echo "Parameter '$i' is alphabetical"
man "$i" > /dev/null 2> /dev/null
if [ "$?" -eq 0 ];then
echo "$i might be a function. Try to use man of --help"
fi
else
echo "Parameter '$i' is digital"
fi
done;
When you write a test you need spaces around your brackets.
You can easily find and fix those bugs if you use shellcheck
#!/bin/bash
var="true"
i=1
while $var
do
read -p "Enter value (true/false): " var
if [[ $var == "true" ]]
then
echo "Iteration : $i"
((i++))
elif [[ $var == "false" ]]
then
echo "Exiting the process"
elif [[ $? -eq 1 ]]
then
echo "Invalid Choice."
echo "Avaialable Choices are true or false"
exit
fi
done
Script is Working Fine. I Enter true the loop will iterate for false the script stops.
I want the script will continue asking "Enter Value" if any other value instead of true or false will be entered.
This would do the same with a more academic syntax:
i=0
while :; do
printf 'Enter value (true/false): '
read -r var
case $var in
true)
i=$((i + 1))
printf 'Iteration : %d\n' $i
;;
false)
printf 'Exiting the process\n'
break
;;
*)
printf 'Invalid Choice.\nAvaialable Choices are true or false\n'
;;
esac
done
You might find this to be a cleaner solution:
i=0
while true; do
read -p "enter value: " myinput
if [[ $myinput = true ]]; then
echo "iteration $i"
i=$((i+1))
elif [[ $myinput = false ]]; then
echo "exiting"
exit
else
echo "invalid input"
fi;
done;
The issue I see with your current code is that it is unclear which command's exit status $? refers to. Does it refer to the echo in the previous elif block? Or the last condition check? Or something else entirely?
I'm new in bash. I tried that:
#!/bin/bash
i=1
while [[ $var != "false" ]]
do
read -p "Enter value (true/false): " var
if [[ $var == "true" ]]
then
echo "Iteration : $i"
((i++))
elif [[ $var == "false" ]]
then
echo "Exiting the process"
elif [[ $? -eq 1 ]]
then
echo "Invalid Choice."
echo "Avaialable Choices are true or false"
fi
done
I changed while $var with while [[ $var ]] because while works like if. It runs the given command. In there it is $var's value.
And I moved exit to first elif expression's end. So if user type false program will exit.
I am trying to use this script to be able to shut down my ESXi server based on network connectivity. I found this script online, on the vmcommunity and no one seems to be having the same issues that I am. When I run this script It returns syntax error: unexpected word (expecting "in"). However the in is there
allInterfaces=$(esxcfg-nics -l | grep vmnic | awk '{print $1}' | awk '$1=$1' OFS=" " RS=)
while [ $# -gt 0 ];
do
case "$1" in
-v) verbose=1;;
-t) downCycles=$2; shift;;
-i) shift; break;;
--) shift; break;;
-*) errorLog "wrong parameter:" "$#" ;;
*) break;;
esac
done
shift
Any advice would be extremely helpful. Below if the full script that I am trying to use.
#!/bin/bash
verbose=0
downCycles=3
stateCounter="/tmp/auto-shutdown.counter"
log() {
logger "auto-shutdown: $1"
if [ $verbose -gt 0 ]; then
echo "$1"
fi
}
errorLog() {
logger "auto-shutdown error: $1"
echo -e >&2 "\n$1\n"
echo -e >&2 "auto-shutdown.sh: shutdown vmware ESXi if network interfaces are down for a number of cycles"
echo -e >&2 "-------------------------------------------------------------------------------------------------"
echo -e >&2 "usage: $0 [-v] [-t cycles] [-i vmnic# vmnic# ...]\n"
echo -e >&2 " -v: verbose\n"
echo -e >&2 " vmnic#: selected vmnic interfaces to monitor"
echo -e >&2 " defaults to all available interfaces\n"
echo -e >&2 " cycles: the down condition is reached, if all selected vmnic interfaces"
echo -e >&2 " have been down for <cycles> consecutive executions of this script\n"
echo -e >&2 "(c) 2011-10-22 by Ralf Lueders, support#lrconsult.com"
echo -e >&2 "-------------------------------------------------------------------------------------------------\n"
exit 1
}
if [ ! -e $stateCounter ]; then
echo 0 > $stateCounter
fi
allInterfaces=$(esxcfg-nics -l | grep vmnic | awk '{print $1}' | awk '$1=$1' OFS=" " RS=)
while [ $# -gt 0 ];
do
case "$1" in
-v) verbose=1;;
-t) downCycles=$2; shift;;
-i) shift; break;;
--) shift; break;;
-*) errorLog "wrong parameter:" "$#" ;;
*) break;;
esac
done
shift
selectedInterfaces=$*
nosi=0
for interface in $selectedInterfaces; do
if [ "$(expr "$allInterfaces" : ".*$interface.*")" -eq 0 ]; then
errorLog "$0 error: interface $interface does not exist"
fi
nosi=$( $nosi + 1 )
done
if [ ${#selectedInterfaces} -eq 0 ]; then
selectedInterfaces=$allInterfaces
fi
if [ $verbose -gt 0 ]; then
echo "monitoring interface(s) $selectedInterfaces ..."
fi
downState=1
for interface in $selectedInterfaces; do
state=$(esxcfg-nics -l | grep "$interface" | awk '{print tolower($4)}')
if [ "$state" = "up" ]; then
downState=0
fi
done
count=$(cat $stateCounter)
if [ "$count" -eq "$downCycles" ]; then
log "initiating system shutdown..."
echo 0 > $stateCounter
/sbin/shutdown.sh
/sbin/poweroff
else
if [ $downState -eq 1 ]; then
count=$( $count + 1 )
countDown=$( "$downCycles - $count" )
log "the interface(s) $selectedInterfaces have now been down for $count cycles, $countDown cycles left before system shutdown."
else
count=0
fi
fi
echo $count > $stateCounter
I have used Charles' suggestions and I am no longer getting errors in ShellCheck. However I am still getting the same error I encountered before where it say that I am missing an in that is clearly there. Anyone see anything else that could be edited to solve this issue. I am really not familiar with shell scripting at all, so any information would be helpful.
I have a bash script that asks the user for their details.
I'm setting a limit to how long we wait for the input. I've found this and it appears to what I want.
timelimit=5
echo -e " You have $timelimit seconds\n Enter your name quickly: \c"
name=""
read -t $timelimit name
#read -t $timelimit name <&1
# for bash versions bellow 3.x
if [ ! -z "$name" ]
then
echo -e "\n Your name is $name"
else
echo -e "\n TIME OUT\n You failed to enter your name"
fi
It shows "You have 5 seconds..." any way to update the output so it shows 4,3,2,1 etc as it counts down ?
Thanks
I have tried most of these answers and none of them worked perfectly for me.
Been playing with this for a local developer deployment script.
This solves a few of the issues noted, like including printed output, etc.
Also wrapped as a function for portability. I'm keen to see any improvements.
Here is my solution:
#!/bin/bash
# set -euo pipefail
READTIMEOUT=5
function read_yn {
MESSAGE=$1
TIMEOUTREPLY=$2
NORMALREPLY="Y"
if [ -z "${TIMEOUTREPLY}" ]; then
TIMEOUTREPLY="Y"
fi
TIMEOUTREPLY_UC=$( echo $TIMEOUTREPLY | awk '{print toupper($0)}' )
TIMEOUTREPLY_LC=$( echo $TIMEOUTREPLY | awk '{print tolower($0)}' )
if [ "${TIMEOUTREPLY_UC}" == "Y" ]; then
NORMALREPLY="N"
fi
NORMALREPLY_UC=$( echo $NORMALREPLY | awk '{print toupper($0)}' )
NORMALREPLY_LC=$( echo $NORMALREPLY | awk '{print tolower($0)}' )
for (( i=$READTIMEOUT; i>=0; i--)); do
printf "\r${MESSAGE} [${NORMALREPLY_UC}${NORMALREPLY_LC}/${TIMEOUTREPLY_UC}${TIMEOUTREPLY_LC}] ('${TIMEOUTREPLY_UC}' in ${i}s) "
read -s -n 1 -t 1 waitreadyn
if [ $? -eq 0 ]
then
break
fi
done
yn=""
if [ -z $waitreadyn ]; then
echo -e "\nNo input entered: Defaulting to '${TIMEOUTREPLY_UC}'"
yn="${TIMEOUTREPLY_UC}"
else
echo -e "\n${waitreadyn}"
yn="${waitreadyn}"
fi
}
read_yn "TESTING" "y"
GIST: https://gist.github.com/djravine/7a66478c37974940e8c39764d59d35fa
LIVE DEMO: https://repl.it/#DJRavine/read-input-with-visible-countdownsh
This should work and shouldn't overwrite input, bit more long winded than the other solutions.
#!/bin/bash
abend()
{
stty sane
exit
#Resets stty and then exits script
}
DoAction(){
stty -echo
#Turn off echo
tput sc
#Save cursor position
echo -ne "\033[0K\r"
# Remove previous line
tput cuu1
#Go to previous line
tput el
#clear to end of line
echo "You have $(($time-$count)) seconds"
#Echo timer
echo -n "$Keys"
#Echo currently typed text
stty echo
#turn echo on
tput rc
#return cursor
}
main()
{
trap abend SIGINT # Trap ctrl-c to return terminal to normal
stty -icanon time 0 min 0 -echo
#turn of echo and set read time to nothing
keypress=''
time=5
echo "You have $time seconds"
while Keys=$Keys$keypress; do
sleep 0.05
read keypress && break
((clock = clock + 1 ))
if [[ clock -eq 20 ]];then
((count++))
clock=0
DoAction $Keys
fi
[[ $count -eq $time ]] && echo "you have run out of time" && abend
done
stty sane
echo Your username was $Keys
echo "Thanks for using this script."
exit 0
}
main
This seems to work:
$ cat test.sh
total=5 # total wait time in seconds
count=0 # counter
while [ ${count} -lt ${total} ] ; do
tlimit=$(( $total - $count ))
echo -e "\rYou have ${tlimit} seconds to enter your name: \c"
read -t 1 name
test ! -z "$name" && { break ; }
count=$((count+1))
done
if [ ! -z "$name" ] ; then
echo -e "\nyour name is $name"
else
echo -e "\ntime out"
fi
#!/bin/bash
timelimit=6
name=""
for (( i = 1 ; i <= $timelimit; i++ )); do
echo -ne "\rYou have $(expr $timelimit - $i) seconds. Enter your name quickly: \c"
[ ! -z "$name" ] && { break ; }
read -t 1 name
done
if [ -z "$name" ]; then
echo -e "\n TIME OUT\n You failed to enter your name"
else
echo -e "\n Your name is $name"
fi
this should work
This works fine and fast for me:
#!/bin/bash
#Sets starttimestamp
starttime=$(date +%s)
#Sets timeout
timeout=5
#sets successflag default to false
success=false
#Save Cursorposition
echo -n -e "\033[s"
#While time not up
while [ $(($starttime+$timeout)) -gt $(date +%s) ] ; do
#Return to saved Cursorpositon
echo -n -e "\033[u"
#Display time left
echo "$(((starttime+timeout)-$(date +%s))) seconds left"
#Ask for 1 char then go on in loop make it look like an ongoing input by adding the user variable to the prompt
if read -p foo="Username: $user" -n 1 -t 1 c ; then
#If user hits return in time c will be empty then break out of loop and set success true
if [[ $c == "" ]] ; then
success=true
break
fi
# Append latest character to user variable
user=${user}${c}
unset c
fi
done
if $success ; then
echo "Yiha!"
else
echo "Too late!"
fi
I have a test script the needs to read the variable 'LAB' and e-mail the correct company.
I've looked but can't find anything that has worked yet.
Any thoughts?
#!
#
LAB=3
#
if [ "$LAB" = "$1" ];then
echo "Got Zumbrota" && ./mailZ
fi
#
if [ "$LAB" = "$2" ];then
echo "Got Barron" && ./mailB
fi
#
if [ "$LAB" = "$3" ];then
echo "Got Stearns" && ./mailS
fi
If this a bash script, start your file with
#!/bin/bash
and use -eq for integer comparison and since LAB is an integer in your script
if [ $LAB -eq $1 ]
These cascading if statements can be condensed into a case statement:
case "$LAB" in
1) echo "Got Zumbrota" && ./mailZ
;;
2) echo "Got Barron" && ./mailB
;;
3) echo "Got Stearns" && ./mailS
;;
*) echo "don't know what to do with $LAB"
;;
esac