new to Bash. Simple script - linux

I am new to bash scripting and I am practicing some code.
I am trying to create a script that displays the following output and loops until the user types x or X
Menu 1
C) Calculation
X) Exit
C
Menu 2
Enter an integer or press X to exit:
22
Menu 3
+) Add
-) Subtract
+
Menu 2
Enter an integer or press X to exit:
33
The sum of 22 and 33 is 55
Menu 1
C) Calculation
X) Exit
c
Menu 2
Enter an integer or press X to exit:
50
Menu 3
+) Add
-) Subtract
-
Menu 2
Enter an integer or press X to exit:
23
The difference of 50 and 23 is 27
Menu 1
C) Calculation
X) Exit
X
Here is my code for it:
add ()
{
((sum=n1 + n2))
echo "The sum of $n1 and $n2 is $sum"
exit
}
subtract ()
{
((difference=n1 - n2))
echo "The difference of $n1 and $n2 is $difference"
exit
}
while true
do
echo "Menu 1"
echo "C) Calculation"
echo "X) Exit"
read opr
if [ ${opr} = 'x' ] || [ ${opr} = 'X' ]
then
break
elif [ ${opr} = 'c' ] || [ ${opr} = 'C' ]
then
echo "Menu 2"
echo "Enter an integer or press X to exit:"
fi
read n1
if [ $n1 = 'x' ] || [ $n1 = 'X' ]
then
break
else
echo "Menu3"
echo "+) Add"
echo "-) Subtract"
fi
read opr
if [ $opr = '+' ]
then
echo "Please enter another integer to perform addition"
read n2
add
elif [ $opr = '-' ]
echo "Please enter another integer to perform subtraction"
read n2
subtract
fi
done
I am receiving this error message:
./myscript.sh: line 72: syntax error near unexpected token fi'
./myscript.sh: line 72:fi'
I believe if I make menu1, menu2, and menu3 into functions I could achieve what I desire my output to be instead of this version of it.
But I know that I will still have a problem with those fi ... any idea where I should put them or what do I need to do for my code to work and not give an error?
Thanks
Pill

You simply forgot an then after elif
Look the general example from http://www.thegeekstuff.com/2010/06/bash-if-statement-examples/
If [ conditional expression1 ]
then
statement1
statement2
.
elif [ conditional expression2 ]
then
statement3
statement4
.
.
.
else
statement5
fi
In your code:
...
elif [ $opr = '-' ]
then <<<<<<< missing in your code!
echo "Please enter another integer to perform subtraction"
read n2
subtract
fi
EDIT: Why the program did not loop
If you write exit in your add and subtract method, your script will exit. Why it should not? Tip: remove both exit and the loop have a chance :-)

Related

Ignore the 1st row(header) in a csv files using shell script

Following is my csv file
Contact Id,Customer Code,Billing Account Code,Bank BSB,Bank ID
2222222220,2222222222222220,100,084004,fjksanfjkdsksdnfnkjsnQ==
3333333330,3333333333333330,100,084789,sklsnfksnkdfnkgndfkjgn==
and this is the code I'm using to ignore header & any row which has no data in any of 5 columns
while IFS=',' read cont_id cust_code bac bsb bid
do
if [ "$cont_id" == "" ] || [ "$cust_code" == "" ] || [ "$bac" == "" ] || [ "$bsb" == "" ] || [ "$bid" == "" ]; then
echo $cont_id,$cust_code,$bac,$bsb,$bid >> $SOURCE_DIR/dummyRejectedRecords.csv
elif [ "$cont_id" == "Customer Code" ] && [ "$cust_code" == "Customer" ] && [ "$bac" == "Billing Account Code" ] && [ "$bsb" == "Bank BSB" ] &&[ "$bid" == "Bank ID" ]; then
echo $cont_id,$cust_code,$bac,$bsb,$bid >> $SOURCE_DIR/dummyRejectedRecords.csv
else
echo "Contact_Id = '"$cont_id"'"
echo "Customer_Code = '"$cust_code"'"
echo "Billing_Account_Code = '"$bac"'"
echo "Bank_ID = '"$bsb"'"
echo "Bank_BSB = '"$bid"'"
echo ""
#ADD YOUR PROCEDURE HERE
fi
done < $SOURCE_DIR/dummy.csv
The problem is that 1st row is not being ignored
and this is being appended to 1x1 value of csv Customer Code = 'Customer Code'
even if header is ignored the 1st value of next row is appended with 
Could someone help me here (without using awk command)
Thanks a ton in advance
The first thing that comes to mind is to use a simple control variable to skip the first iteration, provided that the file always contains a header that has to be ignored as the first line.
Add this before the first if statement inside the loop:
if [ -z "$HEADER_DONE" ]; then
HEADER_DONE=1
continue
fi

"Attempted assignment to a non-variable" in bash

I'm new to Bash and I've been having issues with creating a script. What this script does is take numbers and add them to a total. However, I can't get total to work.It constantly claims that total is a non-variable despite it being assigned earlier in the program.
error message (8 is an example number being entered)
./adder: line 16: 0 = 0 + 8: attempted assignment to non-variable (error token is "= 0 + 8")
#!/bin/bash
clear
total=0
count=0
while [[ $choice != 0 ]]; do
echo Please enter a number or 0 to quit
read choice
if [[ $choice != 0 ]];
then
$(($total = $total + $choice))
$(($count = $count + 1))
echo Total is $total
echo
echo Total is derived from $count numbers
fi
done
exit 0
Get rid of some of the dollar signs in front of the variable names. They're optional inside of an arithmetic context, which is what ((...)) is. On the left-hand side of an assignment they're not just optional, they're forbidden, because = needs the variable name on the left rather than its value.
Also $((...)) should be plain ((...)) without the leading dollar sign. The dollar sign will capture the result of the expression and try to run it as a command. It'll try to run a command named 0 or 5 or whatever the computed value is.
You can write:
((total = $total + $choice))
((count = $count + 1))
or:
((total = total + choice))
((count = count + 1))
or even:
((total += choice))
((count += 1))

Bash kdialog Input Box Not Clossing? Possible bad return from msgbox

I am coding in bash ,using Ubuntu 18.04, and I am playing around with kdialog. I made a simple magic eight ball themed program and I am unable to close the input box and exit the program, instead I get stuck in a loop. This code was originally made in BASH dialog and I decided to change it to kdialog. Any help would be greatly appreciated. It is something simple that I am overlooking.
#!/bin/bash
#version 3
OUTPUT="TEMP.txt"
>$OUTPUT
while [ true ]
do
shuffle() {
local i tmp size max rand
size=${#array[*]}
max=$(( 32768 / size * size ))
for ((i=size-1; i>0; i--));
do
while (( (rand=$RANDOM) >= max ));
do :;
done
rand=$(( rand % (i+1) ))
tmp=${array[i]}
array[i]=${array[rand]}
array[rand]=$tmp
done
}
array=( 'It Is Certain'
'Without A Doubt'
'Maybe'
'Signs Point To Yes'
'Most Likely'
'As I See It, Yes'
'Ask Again Later'
'Concentrate And Ask Again'
'HAHAH No..'
'Ask Again'
'Have Faith In Yourself'
'Very Doubtful'
'Outlook Not So Good'
'My Sources Say No'
'Unknown At This Time'
'Could Happen Any Moment Now'
'Is That A Joke?'
'Unlikely' )
shuffle
function sayhello(){
local n=${array[#]}-""
#display it
kdialog --msgbox "This Is What I See: ${array}"
#--clear --msgbox "${array}" 8 41
}
# show an inputbox
kdialog --title "Welcome " \
--inputbox "Ask and you shall recieve great fortune: " #8 60
function think_tank(){
progress=$(kdialog --progressbar "hmmm Let Me Think..." 4);
sleep 1;
qdbus $progress Set "" value 1 > /dev/null;
sleep 1;
qdbus $progress Set "" value 2 > /dev/null;
sleep 1;
qdbus $progress Set "" value 3 > /dev/null;
sleep 1;
qdbus $progress Set "" value 4 > /dev/null;
sleep 1;
qdbus $progress close > /dev/null;
sleep 1
#kdialog --title "This is a passive popup" --passivepopup \
#"It will disappear in about 10 seconds" 10
}
# get response
response=$?
# get data stored in $OUPUT using input redirection
name=$(<$OUTPUT)
case $response in
0)
think_tank
sayhello ${array[#]}
;;
1)
echo "Goodbye For Now."
exit 0
;;
255)
echo "Goodbye For Now."
exit 0
;;
esac
#rm $OUTPUT
done
done
After some sleep I easily figured this issue out. I removed the case statement and used if statements instead. The program would not break out of the case statement due to a return 0 from kdialog's --msgbox.
#made some quick msgbox functions
if [ "$?" = 0 ];
then
think_tank #progress bar
msg_box #results
elif [ "$?" = 1 ];
then
goodbye #closing message box
exit 0;
else
error #error message box
exit 0;
fi;

Setting variable for use later in a case construct

I have this practice problem that is asking me to take my existing code and insert variables for letter grades into the existing 'IF' statements for use in a case construct that reads off a sentence based on the letter grade assigned. The problem I'm having is how to set the variable in the first place, ie. do I need to do a whole new IF/ECHO line for the variable? How do I even word it in the first place?
Would it be something like grade=A?
Here's what I have so far: (EDITED TO INCLUDE SUGGESTIONS)
#!/bin/bash
# Bash shell script to calculate student average
# Usage: ./grade1.sh
# Declare some integer type variables
declare -i test1
declare -i test2
declare -i test3
declare -i test4
declare -i lab
declare -i sum
echo
echo "=================="
echo "Grade Calculator "
echo "=================="
echo
read -p "Enter first name: " firstname
read -p "Enter last name: " lastname
echo
read -p "Enter test score 1: " test1
read -p "Enter test score 2: " test2
read -p "Enter test score 3: " test3
read -p "Enter test score 4: " test4
read -p "Enter lab score: " lab
sum=$test1+$test2+$test3+$test4+$lab
average=$((sum/5))
VAR1=A
VAR2=B
VAR3=C
VAR4=D
VAR5=F
if [ $average -ge 90 ]; then
echo "Course grade: $VAR1"
elif [ $average -ge 80 ]; then
echo "Course grade: $VAR2"
elif [ $average -ge 70 ]; then
echo "Course grade: $VAR3"
elif [ $average -ge 60 ]; then
echo "Course grade: $VAR4"
elif [ $average -le 60 ]; then
echo "Course grade: $VAR5"
fi
echo
echo "Grade results . . ."
echo "Student name: $firstname $lastname"
echo "Total points: $sum"
echo "Course average: $average"
echo
case $grade in
A) echo "An 'A' represents superior course work."
;;
B) echo "A 'B' represents above average course work."
;;
C) echo "A 'C' represents average course work."
;;
D) echo "A 'D' represents below average course work."
;;
F) echo "An 'F' represents failing course work."
;;
esac
The task, as you stated it, doesn't make very much sense .... it would make sense if we have the number of grades and the shresholds completely variable. But, well, it's a practice problem, so they might require you doing some nonsense, just for the exercise.
As it is an exercise, I'll give you some pointers, but don't write down the whole solution.
Assuming that you are really supposed to do what you are asking here (and did not misunderstood the task), you are supposed to replace the literal grades (A, B, C, D, F) by variables. Since you have 5 grades, you need either 5 variables or an array of 5 elements. You asked for variables, so for this exercise, this is the way to go.
Since you have 5 variables, you need to invent 5 different names, for example
this=A
is=B
a=C
silly=D
exercise=F # Note: grade E does not exist
You can write these definition somewhere before they are first used, and feel free to use variable names which suit you better. Now bash knows about these variables, you can use them, for instance:
elif [ $average -ge 60 ]; then
echo "Course grade: $silly"
Now to two things you didn't ask for, but might be interested to know:
First, the calculation of average in your code is incorrect. If you set all tests and the lab to 1, you will get an average of 5 (try it out).
Second, in your case statement, you are using a variable grade, which you don't set anywhere. For example, at the place that you found out that the grade is D (and you did find this out, because you do an echo of this fact), you should set the variable
grade=$silly
This is a circumstance where you can let the shell (bash specifically) help you with your problem by utilizing arrays to help you build your text output and let character classes help with matches in your case statement.
For example, since you know you are grading on the traditional A-F 90-50 breakpoints, you can create several arrays to allow you index all associated information, e.g.
ltrgrades=( A B C D F )
numgrades=( 90 80 70 60 50 )
prefixes=( An A A A An )
comments=( "superior"
"above average"
"average"
"below average"
"failing" )
A function can handle all your output needs per-student by passing the associated index as its first argument:
results() {
echo
echo "Grade results . . ."
echo "Student name : $firstname $lastname"
echo "Total points : $sum"
echo "Course average: $average"
echo
echo "Course grade : ${ltrgrades[$1]}"
echo
echo "${prefixes[$1]} '${ltrgrades[$1]}' represents ${comments[$1]} course work."
}
Utilizing a heredoc simplifies providing multi-line output:
## use a heredoc for multi-line text
cat << EOF
"=================="
"Grade Calculator "
"=================="
EOF
(note: you could utilize a heredoc within the results function)
Finally, you can use character classes as your case matches and '*' to mark the default case, e.g.
case "${average%.*}" in
1?? ) results 0;;
9[0-9] ) results 0;;
8[0-9] ) results 1;;
7[0-9] ) results 2;;
6[0-9] ) results 3;;
* ) results 4;;
esac
Putting it altogether, you could do:
#!/bin/bash
# Bash shell script to calculate student average
# Usage: ./grade1.sh
ltrgrades=( A B C D F )
numgrades=( 90 80 70 60 50 )
prefixes=( An A A A An )
comments=( "superior"
"above average"
"average"
"below average"
"failing" )
results() {
echo
echo "Grade results . . ."
echo "Student name : $firstname $lastname"
echo "Total points : $sum"
echo "Course average: $average"
echo
echo "Course grade : ${ltrgrades[$1]}"
echo
echo "${prefixes[$1]} '${ltrgrades[$1]}' represents ${comments[$1]} course work."
}
## use a heredoc for multi-line text
cat << EOF
"=================="
"Grade Calculator "
"=================="
EOF
read -p "Enter first name : " firstname
read -p "Enter last name : " lastname
echo
read -p "Enter test score 1: " test1
read -p "Enter test score 2: " test2
read -p "Enter test score 3: " test3
read -p "Enter test score 4: " test4
read -p "Enter lab score : " lab
sum=$((test1 + test2 + test3 + test4 + lab))
average=$(echo "scale=2; $sum / 5" | bc)
case "${average%.*}" in
1?? ) results 0;;
9[0-9] ) results 0;;
8[0-9] ) results 1;;
7[0-9] ) results 2;;
6[0-9] ) results 3;;
* ) results 4;;
esac
Example Use/Output
$ bash grades.sh
"=================="
"Grade Calculator "
"=================="
Enter first name : John
Enter last name : Doe
Enter test score 1: 85
Enter test score 2: 93
Enter test score 3: 94
Enter test score 4: 91
Enter lab score : 92
Grade results . . .
Student name : John Doe
Total points : 455
Course average: 91.00
Course grade : A
An 'A' represents superior course work.
$ bash grades.sh
"=================="
"Grade Calculator "
"=================="
Enter first name : Mary
Enter last name : Jane
Enter test score 1: 86
Enter test score 2: 93
Enter test score 3: 72
Enter test score 4: 71
Enter lab score : 77
Grade results . . .
Student name : Mary Jane
Total points : 399
Course average: 79.80
Course grade : C
A 'C' represents average course work.
$ bash grades.sh
"=================="
"Grade Calculator "
"=================="
Enter first name : Sally
Enter last name : Smith
Enter test score 1: 55
Enter test score 2: 61
Enter test score 3: 42
Enter test score 4: 58
Enter lab score : 59
Grade results . . .
Student name : Sally Smith
Total points : 275
Course average: 55.00
Course grade : F
An 'F' represents failing course work.
Since the grades are computed as a floating-point value using bc, you can, and probably should, handle rounding (e.g a 79.5 rounds to 80 while a 79.4 remains 79. You can handle that with another variable score with something similar to:
sum=$((test1 + test2 + test3 + test4 + lab))
average=$(echo "scale=2; $sum / 5" | bc)
fract=${average#*.}
score=${average%.*}
(( ${fract:0:1} >= '5')) && ((score++))
case $score in
1?? ) results 0;;
9[0-9] ) results 0;;
8[0-9] ) results 1;;
7[0-9] ) results 2;;
6[0-9] ) results 3;;
* ) results 4;;
esac
Now Mary Jane's grade with an average of 79.80 is rounded to 80 a B instead of a C. It's up to you to determine how rounding is handled, this is just one way to approach it. You could re-write results to show both the computed average and rounded score, e.g.
results() {
cat << EOF
Grade results . . .
Student name : $firstname $lastname
Total points : $sum
Course average: $average ($score)
Course grade : ${ltrgrades[$1]}
${prefixes[$1]} '${ltrgrades[$1]}' represents ${comments[$1]} course work.
EOF
}

Grub 2, changing shift button

I'm new to linux and bash, so I don't know what am I doing wrong. I set up grub to don't show, and show after pushing shift for 3 seconds. This worked fine. Then I changed line in 30_os-prober (as you can see underneath) which contained 'shift' to 'F11' as I read here: http://www.gnu.org/software/grub/manual/grub.html (13.3.33). Now when I press F11 nothing happens, and when I press shift I can see 'Grub is loading', then default OS (Ubuntu) loads without showing grub menu.
This is part of my /etc/default/grub content:
# If you change this file, run 'update-grub' afterwards to update
# /boot/grub/grub.cfg.
GRUB_DEFAULT="Custom Menu"
GRUB_HIDDEN_TIMEOUT=1
GRUB_HIDDEN_TIMEOUT_QUIET=true
GRUB_TIMEOUT=0
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="splash"
GRUB_CMDLINE_LINUX=" splash vga=799 quiet"
and this is (IMHO) crucial part, from /etc/grub.d/30_os-prober:
adjust_timeout () {
#if [ "x${found_other_os}" = "x" ] ; then
if [ "x${GRUB_HIDDEN_TIMEOUT}" != "x" ] ; then
if [ "x${GRUB_HIDDEN_TIMEOUT_QUIET}" = "xtrue" ] ; then
verbose=
else
verbose=" --verbose"
fi
if [ "x${GRUB_HIDDEN_TIMEOUT}" = "x0" ] ; then
cat <<EOF
if [ \${timeout} != -1 ]; then
if keystatus; then
if keystatus --F11; then << There I changed shift to F11
set timeout=-1
else
set timeout=0
fi
else
if sleep$verbose --interruptible 3 ; then
set timeout=0
fi
fi
fi
EOF
else
cat << EOF
if [ \${timeout} != -1 ]; then
if sleep$verbose --interruptible ${GRUB_HIDDEN_TIMEOUT} ; then
set timeout=0
fi
fi
EOF
fi
fi
#fi
}
Thank you.
The keystatus documentation at the link you provided (and other keystatus documentation that I've come across) indicates that it accepts only --shift --ctrl or --alt as key-specific parameters. Perhaps it does not work for other keys such as F11.
The other thing to be aware of is that keystatus apparently does not work on all platforms. When that is the case, your first keystatus call will return false and the rest of the keystatus conditional logic will be skipped.

Resources