How to update variable within case statement based on user input - linux

I have created a bash script which is essentially a wizard with a number of questions, some are multiple choice. When a multiple choice question is presented, I want the user to be able to choose a number, where each number corresponds to a different answer. I want this answer to be the variable which can be later used in the script.
I realize this is what the 'select' function is used for, but I also have a requirement when the user simply hits [ENTER] a default value is assumed. As far as I know, the 'select' function assumes an empty value of "" when the [ENTER] key is pressed (an invalid option) and re-prompts the question.
The code below is me attempting to update a variable when a number is pressed. For example when the number '1' is pressed, I want the $hash variable to updated to 'sha224'.
Is there any to achieve this using a case statement without 'select'? If not what are my alternatives?
echo
echo "Select a hashing algorithm"
echo "1 - sha224"
echo "2 - sha256"
echo "3 - sha384"
echo "4 - sha512"
while true; do
read -p "Option: [sha256]:" -e hash
case $hash in
"") hash="sha256" break 2;;
1) hash="sha224" break 2;;
2) hash="sha256" break 2;;
3) hash="sha384" break 2;;
4) hash="sha512" break 2;;
*) echo "Invalid Response: Please enter [1-4] and hit [ENTER] or hit [ENTER] to select 'sha256'";;
esac
done

Insert ; before all break.
Replace $hash with ${hash:=2} to use a default value if $hash is empty.
"") hash="sha256" break 2;; can be removed.

Related

How do I collect arguments from the command-line? [duplicate]

This question already has answers here:
How do I parse command line arguments in Bash?
(40 answers)
Closed 3 years ago.
I am very new to shell scripting and using CLI all together. However, I want to create a simple program that can collect arguments from a user.
Optimally, I want my shell script to get a network name and instance name from the user, and assign the values to variables.
I know that $# can be used to gather arguments, but are there any other ways? I often notice when I make a command, there is, for example, something like this: $create --instance_name NAME . Can I gather an argument by using -- to specify the parameter? If so, here is an example of the command:
$collect_variable.sh --network NETWORK_NAME --instance_name INSTANCE_ID
Once again, thank you for any help. I am very new to stack overflow and unix all together, and any help is appreciated.
You can using a variable to assign to for example:
while getopts "x:y:z:" res
do
case "$res" in
x ) paramX="$OPTARG" ;;
y ) paramY="$OPTARG" ;;
z ) paramX="$OPTARG" ;;
? ) echo "not a valid value"
esac
done
Check the rest of the links that other StackO users sent, they provide great examples!
Hope that can help you solve your issue.
Peace!
There are lots of ways of gathering arguments. You can name their position, e.g. $1 is the first argument and $14 is the fourteenth.
You can refer to the argument list like $# (this preserves spacing) or like $* (this collapses spacing):
test_args() {
echo '# as "$#"'
for argument in "$#"; do
echo "$argument"
done
echo '# as "$*"'
for argument in "$*"; do
echo "$argument"
done
echo '# as $*'
for argument in $*; do
echo "$argument"
done
}
$ test_args "1 2" three four
# as "$#"
1 2
three
four
# as "$*"
1 2 three four
# as $*
1
2
three
four
Since you're exclusively using long options separated from their arguments by spaces,
while [ "$#" -gt 0 ]; do
case "$1" in
( --network ) NETWORK="$2"; shift ;;
( --network=?* ) NETWORK="${1#*=}" ;;
( --instance_name ) INSTANCE_NAME="$2"; shift ;;
( --instance_name=?* ) INSTANCE_NAME="${1#*=}" ;;
( -* ) echo "Illegal option -- $1" >&2; exit 2 ;;
( * ) break ;; # perhaps non-options are used later
esac
shift
done
This loops on each option and parses it. There are two conditions for each option, which lets us handle when the arguments are spaced from the options (--network bobnet) or when they're assigned to the options (--network=bobnet). Spaced means we need to refer to the next argument (thus the extra shift) while assigned means we need to use parameter subsitution to remove the characters at the front of the string up until (and including) the first =.
The final shift pulls the first item off of the argument list so we can operate on the next one. Shifting separately for the two-argument clauses (rather than ending with shift 2) also allows for binary options like --verbose, which doesn't contain an argument.
You can also use a while getopts loop to support a mix of short and long arguments; see my more detailed post on getopts.

Breaking out of nested function loops in Bash

I am using a nested function to partition and making the filesystem for drives attached to a new Linux box.
I am having a strange issue trying to break out of all loops.
I am keeping track of the nested loop index and using "break n".
When the user replies "n" to the question "Do you have any additional drives to partition?" i expect to break out of all nested loops and continue with the script, but what happens is that the question gets asked again.
Can you help me figure this out?
INIT_STARTED=0
chooseDisks()
{
INIT_STARTED=$((INIT_STARTED+1))
# Choosing which drive to work on
read -p "Please type the name of the disk you want to partition: " DISK
while true; do
read -p "Are you sure you want to continue ? y (partition)/n (choose another drive) /x (continue) " ynx
case $ynx in
[Yy]* )
containsElement "$DISK"
if [ $? == 1 ]; then
initializeDisk $DISK
# remove element from found disk to prevent trying to partition it again.
delete=($DISK)
FOUNDDISKS=( "${FOUNDDISKS[#]/$delete}" )
else
echo "${red}$DISK is not a valid choice, please select a valid disk.${reset}"
chooseDisks
fi
break;;
[Nn]* )
chooseDisks
break $((INIT_STARTED));;
[Xx]* )
return
break;;
* ) echo "Please answer y or n. x to continue the script.";;
esac
done
# Any additional disks to partition?
while true; do
read -p "Do you have any additional drives to partition ? y/n " yn
case $yn in
[Yy]* )
#chooseDisks $FOUNDDISKS
chooseDisks
break $((INIT_STARTED));;
[Nn]* )
return
break $((INIT_STARTED));;
* ) echo "Please answer y or n";;
esac
done
}
I expect this:
break $((INIT_STARTED));;
to end the nth loop and exiting the function.
Don't play with nested logic break, just use some variable like $userStop and instead of while true; do put
userStop = false
while[!${userStop}]
do
#...
# replace break $((INIT_STARTED));; by
# userStop = true
I ended up changing the code to avoid breaking within a loop.
Thanks guys for directing me the right way.
David
i expect to break out of all nested loops and continue with the script
You can run the function in a subshell and use exit.
chooseDisks()
{
if [ "$1" -eq 0 ]; then
echo "The user entered it all!"
exit 0
fi
echo "The user is still entering... $1"
( chooseDisks $(($1 - 1)) )
}
# Imagine the user 5 times enters something
( chooseDisks 5 )
But the best would be to refactor your code to just have a big while true; do loop in the beginning. There is no need to make this function recursive.

Assigning variable to a variable inside if statement

I am trying to assign a variable from a prompt input choice with no luck. If the user inputs 1, I want target_db_name = "database2".
My code:
while true; do
read -p "What is the table name?" table_name
table_name=${table_name,,}
if hdfs dfs -test -e /foo/$table_name ;
then read -p "What is the target database you want to copy the
“foo.${table_name}” table to?
Your three options are:
1) database1
2) database2
3) database3
Type 1, 2, or 3: " target_db;
(((Here is where I want to state if $target_db = "1" then target_db_name
= "database1", if $target_db = "2" then target_db_name = "database2" etc...)))
read -p "Would you like to begin the HDFS copy with the following configuration:
Target Database: ${target_db_name}
Table Name: ${table_name}
Continue (Y/N):"
else echo "Please provide a valid table name.
Exiting this script" ; exit ; fi
done
I have tried creating another if statement with no luck.
"....Type 1, 2, or 3: " target_db;
else if $target_db = "1" then target_db_name = "edw_qa_history"; fi
if $target_db = "1" then won't work, because what follows if must be a command, not a test expression. Now, the most common command used in if statements is [ (yes, that's actually a command name; it's synonymous with the test command), which takes a test expression (and a close bracket) as its arguments and succeeds or fails depending on whether the expression is true or not. So the correct syntax would be something like:
if [ "$target_db" = "1" ]; then
Note that there are two other differences from what you had: I put double-quotes around the variable reference (almost always a good idea, to avoid may parsing oddities), and added a semicolon before then (needed to indicate where the arguments to [ end and shell syntax resumes). I also notice you have semicolons at the end of many lines of your script; this isn't necessary, the end-of-line is enough to indicate the end of a command. It's only if you have another command (or something like then) on the same line that you need a semicolon as a delimiter.
HOWEVER, as #Barmar pointed out in a comment, case would probably be better than a list of if and elif statements here. case is intended specifically for comparing a string against a list of other strings (or patterns), and executing different things depending on which one it matches. It looks something like this:
case "$target_db" in
1) target_db_name="database1" ;;
2) target_db_name="database2" ;;
3) target_db_name="database3" ;;
*) "Please provide a valid table name. Exiting this script" ; exit ;;
esac
Here, the double-semicolon is needed, even at the end of a line, to indicate the end of each case. Also, note that the * pattern (the last case) matches anything, so it functions like an else would in an if ... elif ... sequence.
Final note: use shellcheck.net to sanity-check your code.
You don't need an if statement to map the number to an array; you just need an array.
db_names=(
"datebase 1"
"database 2"
"database 3"
)
# ...
target_db_name=${db_names[$target_db - 1]}
if [[ -z $target_db_name ]]; then
exit
fi

Variable While Loop

I would like to know how to include -, *, and ,/, in the following while loop in addition to the + I have already included. If the user enters something other than +, -, * or / I want the invalid input message to print. However, so far I have only worked out how to include one of the arguments in the code, in this case the +. How do I include the other 3 arguments in the same bit of code? I am a noobie I admit, and I don't currently have the vocabulary to search an answer specific to my needs so thought my best best was writing out the issue.
Any help appreciated. Thanks
echo "Please enter an operation of arithmetic. Press either +, -, * or /"
read operation
while [ $operation != "+" ]; do
echo "sorry, that is an invalid input- re-enter operation of arithmatic"
read operation
You can use that in a while loop like this:
while read -p "Please enter an operation of arithmetic. Press either +, -, * or /: " op &&
[[ $op != [-+/*] ]]; do
echo "sorry, that is an invalid input- re-enter operation of arithmatic"
done
You probably want select here:
PS3="Please enter an operation of arithmetic: "
select op in + - / '*'; do
case $op in
-) echo subtract something ; break ;;
+) echo add something ; break ;;
/) echo divide something ; break ;;
\*) echo multiply something ; break ;;
esac
done

syntax error on linux division and multipication

I am trying to calculate 20% of a where a is input by the user.
echo "Please enter your basic salary"
read a
#HRA
b=`expr (20 / 100)\* $a)`|bc
echo HRA is:$b
What's wrong in this expression, which is generating an error message?
The first problem is that you want to just pass the expression into bc for evaluation, so use echo rather than expr.
Secondly, the order of evaluation you have is going to calculate 20/100 as an integer first, which will be 0, then multiple the salary by that, resulting in 0. Reordering the calculation will resolve that.
So the following should work:
echo "Please enter your basic salary"
read a
#HRA
b=`echo $a " * 20 / 100"|bc`
echo HRA is:$b
Alternatively, if you prefer to use expr rather than bc, you can escape the problem characters to make this work:
echo "Please enter your basic salary"
read a
#HRA
b=`expr $a \* 20 \/ 100`
echo HRA is:$b
You can use let operator also in bash
#!/bin/bash
echo "Please enter your basic salary"
read a
let b=$a*20/100
echo HRA is:$b

Resources