Making a program that takes user input for a number 1-7 and then displays a command according to the number the user chooses. [Bash - CentOS] - linux

As the title says, I'm trying to make a program that has a user input a number 1-7, then displays the appropriate command for each number.
The problem I'm having is finding a good way to set each number to a command.
At first, I thought about doing something like this.
OSI=$(uname -a)
echo $OSI
But the problem with that is actually implementing it into a loop. Let's say the user is prompted like so:
"Enter a number:"
The user enters the number 1, and number 1 is the OSI. Well if a user picks the number 2, it needs to display a different command and so forth.
This is a little bit too complicated for a beginner like myself. I've read through forums and different posts, but I cannot figure out the right commands to make this happen.
I tried doing something like this and it failed miserably:
#!/bin/bash
read -p "Enter a number:" n1 n2 n3 n4 n5 n6 n7
if n1=1; then
uname - a
else n2=2; "different command"
fi
I realize I'm completely garbage at bash. I'm not asking for anyone to solve this, just give me some pointers in a way that makes sense to me.
Thanks.

Give this tested version a try:
#!/bin/bash --
printf "menu items:\n 1) uname -a\n 2) date\n q) exit\n"
read -p "Enter your choice: " response
if [ -z "$response" ] ; then
printf "Choice invalid\n"
exit 1
fi
if [ "$response" = q ] ; then
exit 0
fi
if [ "$response" = 1 ] ; then
uname -a
elif [ "$response" = 2 ] ; then
date
else
printf "Choice invalid\n"
fi
As written by #EdMorton case is a better option.

Related

While going for extra loop

I have the following code to loop through a question session with a user:
#loop counter
COUNTER=1
# Initial question
echo ""
echo -n "Would you like a cup of tea? (y/n)" # Ask initial question
read answer # Check answer
while [ $COUNTER -le 5 ] # while counter is less than or equal to 5
do
if [ "$answer" != "${answer#[Yy]}" ]; then # if answer is 'y'
echo "" # skip a line for cleanliness
echo "Great, I'll make it now" # Satisfaction
echo ""
break # End the loop
else # if answer is anything other than 'y'
echo "" # skip a line for cleanliness
echo -n "Are you sure? (y/n)" # Ask again
read answer # Check the answer again
let COUNTER=COUNTER+1 # increment COUNTER
fi
done # finish
The code should act as follows:
Ask user if they would like tea.
If user says yes, then print out something, and end loop
If user says no, go for another 4 attempts before ending loop.
If user says yes, after saying no, print out something, and end loop.
The code works fine for points 1,2, and 4.
On point 3 - the code loops to a 5th loop before ending.
I can't seem to be able to identify the error.
Edit: Changed to COUNTER = 0, and $COUNTER -le 4. However, code doesn't implement point 4, on 4th attempt (i.e. 1 initial no, followed by 3 extra nos, before a yes).
You could simply limit counter to less than 5, instead of less than or equal to.
Use -lt instead of -le. That is [ $COUNTER -lt 5]
OR
You can say less than or equal to 4 and thus you can maintain [ $COUNTER -le 4]
Instead of having 2 lines,
echo -n "Are you sure? (y/n)" # Ask again
read answer # Check the answer again
you can shorten it to one:
read -p "Are you sure? (y/n): " answer
However, I'm not entirely sure by what you mean by point 4. Could you provide an example or a little more description?

Expanding a string with a variable reference later, after the variable is assigned

I'm trying to combine two lists containing names (if available) and emails with a standard email text in bash (shell)
(I had to delete the irrelevant code as it contains some private info, so some of the code might look unusal.)
The first half of the code checks if there is a name list along with the email list.
The second half combines only the email address and text if no name is available, if the name list is available it also 'tries' to combine the name, email and text.
f1 = email list and f2 = name list.
As you can see in the first half of the code below, $f2 should show the names if the list is available but it does not show anything in the log file.
I been trying to sort this problem out for two days but nothing has worked. When names are available it always outputs as "Hello ..." when it should be "Hello John D..."
#FIRST HALF
if [ "$names" = "no" ]
then
text="Hello..."
elif [ "$names" = "yes" ]
then
text="Hello $f2..."
fi
#SECOND HALF
if [ "$names" = "no" ]
then
for i in $(cat $emaillist); do
echo "$text" >> /root/log
echo "$i" >> /root/log
done
elif [ "$names" = "yes" ]
then
paste $emaillist $namelist | while IFS="$(printf '\t')" read -r f1 f2
do
echo "$text" >> /root/log
echo "$f1" >> /root/log
done
fi
When you run text="Hello $f2", $f2 is looked up at the time of the assignment; an exact string is assigned to text, and only that exact string is used later, on echo "$text".
This is very desirable behavior: If shell variables' values could run arbitrary code, it would be impossible to write shell scripts that handled untrusted data safely... but it does mean that implementing your program requires some changes.
If you want to defer evaluation (looking up the value of $f2 at expansion time rather than assignment), don't use a shell variable at all: Use a function instead.
case $names in
yes) write_greeting() { echo "Hello $name..."; };;
*) write_greeting() { echo "Hello..."; };;
esac
while read -r name <&3 && read -r email <&4; do
write_greeting
echo "$email"
done 3<"$namelist" 4<"$emaillist" >>/root/log
Some enhancements in the code above:
You don't need paste to read from two streams in lockstep; you can simply open them on different file descriptors (above, FDs 3 and 4 are chosen; only 0, 1 and 2 are reserved, so larger numbers could have been selected as well) with a separate read command for each.
Opening your output sink only once for the entire loop (by putting the redirection after the done) is far more efficient than re-opening it every time you want to write a single line.
Expansions, such as "$namelist" and "$emaillist", are always quoted; this makes code more reliable if dealing with filenames with unusual characters (including spaces and glob expressions), or if IFS is at a non-default value.

Difficulty with command line parameters

Welcome all,
I am required to prompt the user to enter "all, long, human or alh". I have developed some code however there is an error. I have used shellcheck.net to check whether the syntax of my code is appropriate.
the program itself should give a file and directory listing (based on the command line parameter received) and also show an error message if one of the options above is not selected.
”all” - do not hide entries starting with .
“long” - use a long listing format
“human” - use a long listing format and print sizes in human readable
format
“alh” – do all of the above
This is my code:
read -p "Please enter all, long, human or alh: " userInput
if [ -z "$userInput" ];
then
print f '%s/n' "Input is not all, long human or alh"
exit 1
else
printf "You have entered %s " "$UserInput"
fi
the code itself works, however it does not show any directory listings based on the chosen parameter.
Output:
Please enter all, long, human or alh: all
You have entered all
I'd use the case command
#!/bin/bash
read -p "Please enter all, long, human or alh: " userInput
case "$userInput" in
(all) flags=a;;
(long) flags=l;;
(human) flags=lh;;
(alh) flags=alh;;
(*) printf '%s\n' "Input is not all, long human or alh"
exit 1;;
esac
ls -$flags
Or, you can store the flags in an associative array:
#!/bin/bash
read -p "Please enter all, long, human or alh: " userInput
declare -A flags
flags=([all]=a
[long]=l
[human]=lh
[alh]=alh
)
flag=${flags[$userInput]}
if [[ -z $flag ]] ; then
printf '%s\n' "Input is not all, long human or alh"
exit 1
fi
ls -$flag

0999: Value too great for base (error token is "0999")

This is a shortened-version of a script for reading 8mm tapes from a EXB-8500 with an autoloader (only 10 tapes at a time maximum) attached. It dd's in tape data (straight binary) and saves it to files that are named after the tape's 4-digit number (exmaple D1002.dat) in both our main storage and our backup. During this time it's logging info and displaying its status in the terminal so we can see how far along it is.
#!/bin/bash
echo "Please enter number of tapes: [int]"
read i
j=1
until [ $i -lt $j ]
do
echo "What is the number of tape $j ?"
read Tape_$j
(( j += 1 ))
done
echo "Load tapes into the tower and press return when the drive is ready"
read a
j=1
until [ $i -lt $j ]
do
k="Tape_$j"
echo "tower1 $j D$(($k)) `date` Begin"
BEG=$j" "D$(($k))" "`date`" ""Begin"
echo "tower1 $j D$(($k)) `date` End"
END=$j" "D$(($k))" "`date`" ""End"
echo "$BEG $END"
echo "$BEG $END"
sleep 2
(( j += 1 ))
done
echo "tower1 done"
Everything was hunky-dory until we got under 1000 (startig at 0999). Error code was ./tower1: 0999: Value too great for base (error token is "0999"). Now I already realize that this is because the script is forcing octal values when I type in the leading 0, and I know I should insert a 10# somewhere in the script, but the question is: Where?
Also is there a way for me to just define Tape_$j as a string? I feel like that would clear up a lot of these problems
To get the error, run the script, define however many tapes you want (at least one, lol), and insert a leading 0 into the name of the tape
EXAMPLE:
./test
Please enter number of tapes: [int]
1
What is the number of tape 1?
0999
./test: 0999: Value too great for base (error token is "0999")
You don't want to use $k as a number, but as a string. You used the numeric expression to evaluate a variable value as a variable name. That's very bad practice.
Fortunately, you can use variable indirection in bash to achieve your goal. No numbers involved, no error thrown.
echo "tower1 $j ${!k} `date` Begin"
BEG=$j" "D${!k}" "`date`" ""Begin"
And similarly in other places.

Bash script looping after end of file

My bash script asks user for their first name, last name, address and phone number and writes this information that is input by the user to a file of the format "firstname.lastname"; however I want to repeat this a number of times (I actually wanted to do something like a do..while loop where it runs atleast one time and asks user if they want to continue creating accounts or not but I see there is no do...while for bash it seems). So when I execute this bash script in the terminal it will ask for how many accounts to be made and I provide all the input but the it only runs one time. What am I doing wrong? Here is my script.
#!bin/bash
echo "How many accounts are you creating?"
read num
echo "Enter first name"
read fName
echo "Enter last name"
read lName
echo "Enter address"
read add
echo "Enter phone number"
read phn
echo "Enter gender m for male and f for female"
read gender
if [ "$gender" == "m" ]
then
sex="male"
elif [ "$gender" == "f" ]
then
sex="female"
else
echo"Invalid gender. Restart the script and enter a valid gender"
exit 0
fi
for (( i = 0; i<=num; i++))
do
cat > $fName.$lName <<-EOF
Full Name: $fName $lName
Address: $add
Phone number: $phn
Gender: $gender
EOF
done
zerobandwidth's answer is correct, but as an alternative answer, it is in fact quite easy to do what you initially wanted to do:
while true; do
# Your account creation code goes here
echo "Create another account (y/n)?"
read another
if [ "$another" != y ]; then
break
fi
done
Put your input code inside the loop, or wrap that code in a function and call the function inside the loop.

Resources