I am writing a bash script for a homework and it is required to build a mini library with some functions. I got almost all functions working except the search function. The if statement in line 11 is always true. I don't know how to compare that.
$lib="library"
function search_book {
echo Enter Book Title
read title
exists=`grep "$title" $library | wc -l`
if (( $exists == 0 ))
then
echo "No Such Book in Library"
else
act_owner=`awk -F, '/'$title'/ {print $3}' $library`
echo $act_owner
if (( $act_owner == $lib ))
then
echo Book Kept In $act_owner
else
echo Book Checked Out by $act_owner
fi
fi
stop=0
while (( $stop == 0 ))
do
echo
echo "=========================="
echo "(t) Try again"
echo "(b) Back to main menu"
echo -n 'Choose Option to Continue'
read reply
case $reply in
"t") stop=1; search_book;;
"b") stop=1; main_menu;;
*) echo illegal choice, enter again:
esac
done
}
This if condition seems to be problem:
if (( $act_owner == $lib ))
Since (( and )) are used for arithmetic operations only and it is always evaluating to true because both operators get converted to numbers and if condition becomes (( 0 == 0 )) and that always evaluates to true.
Even this if condition:
(( 'abc' == 0 )) && date
will evaluate to true and print the date for the same reason.
FIX: Change that condition to:
if [[ $act_owner == $lib ]]
Related
I want to just insert number between two values, and otherwise the script repeated until correct number.
This is my script and it does not work correctly:
validation(){
read number
if [ $number -ge 2 && $number -ls 5 ]; then
echo "valid number"
break
else
echo "not valid number, try again"
fi
}
echo "insert number"
validation
echo "your number is" $number
If you are using Bash, you are better off using the arithmetic expression, ((...)) for readability and flexibility:
if ((number >= 2 && number <= 5)); then
# your code
fi
To read in a loop until a valid number is entered:
#!/bin/bash
while :; do
read -p "Enter a number between 2 and 5: " number
[[ $number =~ ^[0-9]+$ ]] || { echo "Enter a valid number"; continue; }
if ((number >= 2 && number <= 5)); then
echo "valid number"
break
else
echo "number out of range, try again"
fi
done
((number >= 2 && number <= 5)) can also be written as ((2 <= number <= 5)).
See also:
Test whether string is a valid integer
How to use double or single brackets, parentheses, curly braces
Your if statement:
if [ $number -ge 2 && $number -ls 5 ]; then
should be:
if [ "$number" -ge 2 ] && [ "$number" -le 5 ]; then
Changes made:
Quoting variables is considered good practice.
ls is not a valid comparison operator, use le.
Separate single-bracket conditional expressions with &&.
Also you need a shebang in the first line of your script: #!/usr/bin/env bash
if [ $number -ge 2 && $number -ls 5 ]; then
should be
if [[ $number -ge 2 && $number -le 5 ]]; then
see help [[ for details
Try bellow code
echo "Enter number"
read input
if [[ $input ]] && [ $input -eq $input 2>/dev/null ]
then
if ((input >= 1 && input <= 4)); then
echo "Access Granted..."
break
else
echo "Wrong code"
fi
else
echo "$input is not an integer or not defined"
fi
2 changes needed.
Suggested by Sergio.
if [ "$number" -ge 2 ] && [ "$number" -le 5 ]; then
There is no need of break. only meaningful in a for, while, or until loop
while :; do
read option
if [[ $option -ge 1 && $option -lt 4 ]]; then
echo "correct"
c
break
else
echo "Incorrect option selected,choose an option between [1-4]"
fi
done
I want to just insert number between two values, and otherwise the script repeated until correct number.
This is my script and it does not work correctly:
validation(){
read number
if [ $number -ge 2 && $number -ls 5 ]; then
echo "valid number"
break
else
echo "not valid number, try again"
fi
}
echo "insert number"
validation
echo "your number is" $number
If you are using Bash, you are better off using the arithmetic expression, ((...)) for readability and flexibility:
if ((number >= 2 && number <= 5)); then
# your code
fi
To read in a loop until a valid number is entered:
#!/bin/bash
while :; do
read -p "Enter a number between 2 and 5: " number
[[ $number =~ ^[0-9]+$ ]] || { echo "Enter a valid number"; continue; }
if ((number >= 2 && number <= 5)); then
echo "valid number"
break
else
echo "number out of range, try again"
fi
done
((number >= 2 && number <= 5)) can also be written as ((2 <= number <= 5)).
See also:
Test whether string is a valid integer
How to use double or single brackets, parentheses, curly braces
Your if statement:
if [ $number -ge 2 && $number -ls 5 ]; then
should be:
if [ "$number" -ge 2 ] && [ "$number" -le 5 ]; then
Changes made:
Quoting variables is considered good practice.
ls is not a valid comparison operator, use le.
Separate single-bracket conditional expressions with &&.
Also you need a shebang in the first line of your script: #!/usr/bin/env bash
if [ $number -ge 2 && $number -ls 5 ]; then
should be
if [[ $number -ge 2 && $number -le 5 ]]; then
see help [[ for details
Try bellow code
echo "Enter number"
read input
if [[ $input ]] && [ $input -eq $input 2>/dev/null ]
then
if ((input >= 1 && input <= 4)); then
echo "Access Granted..."
break
else
echo "Wrong code"
fi
else
echo "$input is not an integer or not defined"
fi
2 changes needed.
Suggested by Sergio.
if [ "$number" -ge 2 ] && [ "$number" -le 5 ]; then
There is no need of break. only meaningful in a for, while, or until loop
while :; do
read option
if [[ $option -ge 1 && $option -lt 4 ]]; then
echo "correct"
c
break
else
echo "Incorrect option selected,choose an option between [1-4]"
fi
done
I've seen other answers about bash integer checks and comparisons, however the results I'm getting are very confusing to me.
Suppose I have this script:
if [[ $1 -eq $1 ]] ;then
echo "number"
else
echo "not number"
fi
if (( $1 >= 0 )) ;then
echo "number"
else
echo "not number"
fi
If I pass a string for parameter one , I get back "number".
That's because string is understood as a variable whose value is 0, and 0 >= 0 is true. Try with > (but it will report 0 as not number - but it already misclassifies all negative integers).
Cf:
a=1
b=a
x=b
(( x > 0 )) && echo 1
a=0
(( x > 0 )) || echo 0
or even
$ a=x
$ x=a
$ (( x > 0 ))
bash: ((: a: expression recursion level exceeded (error token is "a")
Bash tries hard to resolve the variable:
(
for l in a{1..1023} ; do
printf "$l\n$l="
done
echo 1
echo '((a1>0))'
) | tail -n+2 | bash
I have tried to piece together a script to list different jws directories and allow user to select different client directories before it continues on to edit jnlp files within the directory. I have the edit part working, I have the menu working mostly; I can't figure out how to exit the loop once the selections have been made.
I'd like it to exit once ENTER is hit without a number selection, and continue with the next part of the script.
function update_jnlp
{
while :
do
# JNLP update submenu
options=($(ls /tmp/test/ | grep "jws$"))
menu() {
clear
echo "Locally installed jnlps:"
for i in ${!options[#]}; do
printf "%3d%s) %s\n" $((i+1)) "${choices[i]:- }" "${options[i]}"
done
[[ "$msg" ]] && echo "$msg"; :
}
prompt="Check an option (again to uncheck, ENTER when done): "
while menu && read -rp "$prompt" num && [[ "$num" ]]; do
[[ "$num" != *[![:digit:]]* ]] &&
(( num > 0 && num <= ${#options[#]} )) ||
{ msg="Invalid option: $num"; continue; }
((num--)); msg="${options[num]} was ${choices[num]:+un}checked"
[[ "${choices[num]}" ]] && choices[num]="" || choices[num]="+"
done
printf "You selected"; msg=" nothing"
for i in ${!options[#]}; do
[[ "${choices[i]}" ]] && { printf " %s" "${options[i]}"; msg=""; }
done
done
for i in ${choices[#]}; do
printf "%s\n" ${choices[#]};
echo "fun"
done
echo "#msg"
# here is the script to edit the files now contained as ${choices[#]}
}
I recognize that I am in a menu loop and that I need to validate that input from the read command = "" or that $prompt input is null, and I assume I break from there
Please format your code more readably. As it stands, the erratic indentation makes it close to unreadable. Well, the indentation combined with defining a function in the body of a loop inside another function, and some of the more inscrutable loop bodies I've ever seen. (I thought I'd seen quite a lot; clearly, I've still got some learning to do.)
With the code formatted somewhat more orthodoxly, you have:
function update_jnlp
{
while :
do
# JNLP update submenu
options=($(ls /tmp/test/ | grep "jws$"))
menu() {
clear
echo "Locally installed jnlps:"
for i in ${!options[#]}; do
printf "%3d%s) %s\n" $((i+1)) "${choices[i]:- }" "${options[i]}"
done
[[ "$msg" ]] && echo "$msg"; :
}
prompt="Check an option (again to uncheck, ENTER when done): "
while menu && read -rp "$prompt" num && [[ "$num" ]]; do
[[ "$num" != *[![:digit:]]* ]] &&
(( num > 0 && num <= ${#options[#]} )) ||
{ msg="Invalid option: $num"; continue; }
((num--)); msg="${options[num]} was ${choices[num]:+un}checked"
[[ "${choices[num]}" ]] && choices[num]="" || choices[num]="+"
done
printf "You selected"; msg=" nothing"
for i in ${!options[#]}; do
[[ "${choices[i]}" ]] && { printf " %s" "${options[i]}"; msg=""; }
done
echo "$msg" # Added
break # Added
done
for i in ${choices[#]}; do
printf "%s\n" ${choices[#]};
echo "fun"
done
echo "#msg"
# here is the script to edit the files now contained as ${choices[#]}
}
When I ran it, I selected 1, 3, 7 and then hit return, and the final page of output looked like:
Locally installed jnlps:
1+) abc.jws
2 ) def.jws
3+) ghi.jws
4 ) jkl.jws
5 ) mno.jws
6 ) pqr.jws
7+) stu.jws
8 ) vwx.jws
9 ) xyz.jws
stu.jws was checked
Check an option (again to uncheck, ENTER when done):
You selected abc.jws ghi.jws stu.jws
+
+
+
fun
+
+
+
fun
+
+
+
fun
#msg
Without the added break, you don't get to see what's selected because the outer while : loop clears the screen too quickly. With the echo "$msg" added, you get You selected nothing as you'd want.
The material after the main while : loop is clearly not finalized.
In fact, you don't need the while : loop at all, or the incomplete material. You could use:
function update_jnlp
{
# JNLP update submenu
options=($(ls /tmp/test/ | grep "jws$"))
menu() {
clear
echo "Locally installed jnlps:"
for i in ${!options[#]}; do
printf "%3d%s) %s\n" $((i+1)) "${choices[i]:- }" "${options[i]}"
done
[[ "$msg" ]] && echo "$msg"; :
}
prompt="Check an option (again to uncheck, ENTER when done): "
while menu && read -rp "$prompt" num && [[ -n "$num" ]]; do
[[ "$num" != *[![:digit:]]* ]] &&
(( num > 0 && num <= ${#options[#]} )) ||
{ msg="Invalid option: $num"; continue; }
((num--)); msg="${options[num]} was ${choices[num]:+un}checked"
[[ "${choices[num]}" ]] && choices[num]="" || choices[num]="+"
done
printf "You selected"; msg=" nothing"
for i in ${!options[#]}; do
[[ "${choices[i]}" ]] && { printf " %s" "${options[i]}"; msg=""; }
done
echo "$msg"
}
That's still rather inscrutable, but at least it works more or less sanely.
I found an interesting Bash script that will test if a variable is numeric/integer. I like it, but I do not understand why the "0" is not recognized as a number? I can not ask the author, hi/shi is an anonymous.
#!/bin/bash
n="$1"
echo "Test numeric '$n' "
if ((n)) 2>/dev/null; then
n=$((n))
echo "Yes: $n"
else
echo "No: $n"
fi
Thank you!
UPDATE - Apr 27, 2012.
This is my final code (short version):
#!/bin/bash
ANSWER=0
DEFAULT=5
INDEX=86
read -p 'Not choosing / Wrong typing is equivalent to default (#5): ' ANSWER;
shopt -s extglob
if [[ $ANSWER == ?(-)+([0-9]) ]]
then ANSWER=$((ANSWER));
else ANSWER=$DEFAULT;
fi
if [ $ANSWER -lt 1 ] || [ $ANSWER -gt $INDEX ]
then ANSWER=$DEFAULT;
fi
It doesn't test if it is a numeric/integer. It tests if n evaluates to true or false, if 0 it is false, else (numeric or other character string) it is true.
use pattern matching to test:
if [[ $n == *[^0-9]* ]]; then echo "not numeric"; else echo numeric; fi
That won't match a negative integer though, and it will falsely match an empty string as numeric. For a more precise pattern, enable the shell's extended globbing:
shopt -s extglob
if [[ $n == ?(-)+([0-9]) ]]; then echo numeric; else echo "not numeric"; fi
And to match a fractional number
[[ $n == #(?(-)+([0-9])?(.*(0-9))|?(-)*([0-9]).+([0-9])) ]]