accesing functions from external files bash - bash

This is really simple but since i m still a newbiw in shell scripting, i not able to do this. It is simple, i have a menu driven script in which when the user enters his choice i loop in the case function and execute the selected menu. Uptil now i was writing the entire code in the same script. Now i am putting the code in each menu option in a external script and accessing the code from there. So it is something like this,
#!/bin/bash
. course_func
file=names.dat
[ ! -f $file ] && > $file
while true
do
clear
echo ------------------------------------------
echo 1. Create a record
echo 2. View Records
echo 3. Search for records
echo 4. Delete records that match a pattern
echo ------------------------------------------
echo "Please enter your choice or enter (q) to quit: \c ";read choice
case $choice in
1)
create()
echo Enter to continue; read junk
;;
*)
clear
echo Wat nonsense enter properly
echo Enter to continue: ;read crap
;;
esac
done
Then i created another external script where I wrote the create() function
Code is in the link,
create()
{
while true
do
echo Please enter the name: ;read name
echo Please enter your surname: ;read surname
echo Please enter the address: ;read add
echo Please enter the cell no.: ;read cell
if yesno Do you really wish to save the following data to file
then
echo $name:$surname:$add:$cell >> $file
clear
echo The record has been inserted :D
else
echo The record was not save
fi
if yesno Do you wish to enter another record
then
:
else
xit
fi
done
}
addRecord()
{
}
search()
{
}
delRecord()
{
}
But i am getting an error,
course_func: line 31: syntax error near unexpected token `}'
course_func: line 31: `}'
menu_driven: line 18: syntax error near unexpected token `echo'
menu_driven: line 18: ` echo Enter to continue; read junk'
Line 31 is where I am ending the addRecord function, which is empty function, for the remaining cases. Can we not have a empty function in bash?

Functions may not be empty!
--Advanced Bash-Scripting Guide: Chapter 24. Functions
Instead, try:
addRecord()
{
return
}

Related

Loop in a menu untill a condition or the exit/log out option is selected

I'm writing a bash script where an user can add a new database.
The script needs to check, if the name introduced by the user is valid and after if a database with the same name already exist.
For this I already created 2 functions.
is_valid_name , returns 0 if is valid, 1 if is not valid
is_database, returns 0 if a database with the name introduced by the user exist, and 1 if doesn't exist
I want to offer the user the possibility, in case the name is not valid, or the database already exist, to add a different name or cancel/exit.
I want to do this using a Menu with 2 options:
Add a new database
Exit
The pseudo-code:
-> A:
echo Add a database
read -r $database # get the database name from the user
check the entered name - `is_valid_name'
Branch 1.
if the name is not valid, is_valid_name will show the error and return 1
-> B:
show the Menu with the two options
if the user select Option 1(Add a new database) go back to A(see above in bold)
if the user select option 2(Exit) exist the script
Branch 2
if the name is valid check if database exist, is_database
Branch 2.1
if database exist show the Menu with the two options, go back to B(see above in bold)
Branch 2.2
if database doesn't exist go further and execute other code, like create database, populate database etc
I'm thinking of using a while do loop to check if both the name and exist for the database, and get out of the loop if both are ok and continue the code or if user wants to exist.
I don't know how to set(not as syntax) the loop to catch both conditions
If I understood correctly something like this will do the job:
#!/usr/bin/env bash
function is_entity()
{
# fill in to do what you want
printf "%s\n" "$1"
}
while true
do
echo Add New Entity:
read -r entity
if is_entity "$entity"; then
select TASK in 'Entity exist, use another name' 'Logout'
do
case "$REPLY" in
1)
continue 2
;;
2)
printf "Bye\n"
exit 0
;;
esac
done
fi
done
To begin with, do not use all-uppercase variable names -- those are generally reserved for system use. (it's not that you can't, it's just bad form). Use lower-case variable names for your user-variables.
While it is not 100% clear what the remainder of your script should do, it looks as if you are trying to build a list without duplicates using is_entity() to check whether that 'entity' already exists and returning 0 if it does or 1 if it does not. That part is clear -- what isn't clear is how to make the explanation of how to do it useful to you for the rest of your script.
Let's look at it this way, to check whether one entity exists, there must be a collection of them somewhere. For bash, an array of them make sense. So to check whether an entity already exists within an array, you can do something similar to:
declare -a entity # declare an empty indexed array to hold entities
logout=0 # a flag to handle your 'logout' entry
## check if first argument in entity array
# return 0 if it exists, 1 otherwise
is_entity() {
for i in "${entity[#]}" # loop over array comparing entries
do
[ "$i" = "$1" ] && return 0 # if found, return 0
done
return 1 # otherwise return 1
}
That provides a simple function to check whether a previous element in your entity array is present given the first argument to the function (error handling if no argument is given is left to you)
If you are going to have an array of entities, you will need a way to add them. A second simple add_entity() function can call your is_entity() function and either return 0 if the name chosen is already in the array, or if not, just add the new name to the array and display a slightly different menu letting you know that the entity was "Added" instead of "Exists". Something simple like the following will work:
## add entity to array
# return 0 if it exists, 1 otherwise
add_entity () {
local name
printf "\nenter name: " # prompt for new entity name
read name
is_entity "$name" # check if it exists with is_entity
if [ $? -eq '0' ]
then
return 0 # if so, return 0
else
entity+=( "$name" ) # otherwise add it to array
fi
return 1 # and return 1
}
(note: the use of local for name which insures the name variable is limited to the scope of the function and is unset when the function returns)
The remainder of your script to display either the "Added" menu or your "Exists" menu with the two-choices to either add another (or choose another name) could be implemented with two case statements based on the return from add_entity(). Essentially you will loop continually until logout is chosen, calling add_entity() at the beginning of the loop and then using a case statement based on the return value to determine which menu to display. An outline of the logic would be something like:
while [ "$logout" -eq '0' ] ## main loop -- loop until logout -ne 0
do
add_entity # prompt and check with add_entity/is_entity
case "$?" in # filter return with case
0 ) # if the entered name already existed
## Existed Menu
1 ) # if the entity did not exist, but was added to array
## Added Menu
esac
done
In each case, your "Existed" or "Added" Menu could use a simple select loop and could be something like the following for you "Exists" case:
printf "\nEntity exists - '%s'\n" "${entity[$((${#entity[#]}-1))]}"
select task in "use another name" "logout" # display exists menu
do
case "$task" in
"use another name" ) # select menu matches string
break
;;
"logout" )
logout=1 # set logout flag to break outer loop
break;
;;
"" ) # warn on invalid input
printf "invalid choice\n" >&2
;;
esac
done
;;
To verify the operation and that your entities were collected, you could simply display the contents of the array after you exit the loop, e.g.
printf "\nthe entities in the array are:\n"
for ((i = 0; i < ${#entity[#]}; i++))
do
printf " entity[%2d] %s\n" "$i" "${entity[i]}"
done
Putting all the pieces of the puzzle together, you could handle your logic and display the appropriate menu with a script similar to:
#!/bin/bash
declare -a entity # declare an empty indexed array to hold entities
logout=0 # a flag to handle your 'logout' entry
## check if first argument in entity array
# return 0 if it exists, 1 otherwise
is_entity() {
for i in "${entity[#]}" # loop over array comparing entries
do
[ "$i" = "$1" ] && return 0 # if found, return 0
done
return 1 # otherwise return 1
}
## add entity to array
# return 0 if it exists, 1 otherwise
add_entity () {
local name
printf "\nenter name: " # prompt for new entity name
read name
is_entity "$name" # check if it exists with is_entity
if [ $? -eq '0' ]
then
return 0 # if so, return 0
else
entity+=( "$name" ) # otherwise add it to array
fi
return 1 # and return 1
}
while [ "$logout" -eq '0' ] ## main loop -- loop until logout -ne 0
do
add_entity # prompt and check with add_entity/is_entity
case "$?" in # filter return with case
0 ) # if the entered name already existed
printf "\nEntity exists - '%s'\n" "${entity[$((${#entity[#]}-1))]}"
select task in "use another name" "logout" # display exists menu
do
case "$task" in
"use another name" ) # select menu matches string
break
;;
"logout" )
logout=1 # set logout flag to break outer loop
break;
;;
"" ) # warn on invalid input
printf "invalid choice\n" >&2
;;
esac
done
;;
1 ) # if the entity did not exist, but was added to array
printf "\nEntity added - '%s'\n" "${entity[$((${#entity[#]}-1))]}"
select task in "Add another" "logout" # display added menu
do
case "$task" in
"Add another" )
break
;;
"logout" )
logout=1
break
;;
"" )
printf "invalid choice\n" >&2
;;
esac
done
;;
esac
done
printf "\nthe entities in the array are:\n"
for ((i = 0; i < ${#entity[#]}; i++))
do
printf " entity[%2d] %s\n" "$i" "${entity[i]}"
done
Example Use/Output
Running the script to verify your menus and provide testing of the scripts response to different inputs, you could do something like:
$ bash ~/tmp/entity_exists.sh
enter name: one
Entity added - 'one'
1) Add another
2) logout
#? 1
enter name: one
Entity exists - 'one'
1) use another name
2) logout
#? crud!
invalid choice
#? 1
enter name: two
Entity added - 'two'
1) Add another
2) logout
#? 1
enter name: three
Entity added - 'three'
1) Add another
2) logout
#? 2
the entities in the array are:
entity[ 0] one
entity[ 1] two
entity[ 2] three
Look things over and let me know if you have further questions. It was a bit difficult to just tell you how to check is_entity() without knowing how you have them stored to begin with, but the logic here can be adapted to any number of different circumstances.

How do I chain multiple user prompts together with the ability to go back a prompt?

I am wondering how I can make a bash script that has multiple menus in it.
For example, here's what the user would see upon running it:
Type the number of choosing:
1-play
2-load
3-exit
1
What is your name:
::prev::
Type the number of choosing:
1-play
2-load
3-exit
1
What is your name:
Brad
Where are you from, Brad?
Pennsylvania
What is your favourite colour?
1-blue
2-red
3-green
4-grey
5-magenta
,sdfhljalk:J;
What is your favourite colour?
1-blue
2-red
3-green
4-grey
5-magenta
2
What is your favourite toy?
train
What would you like on your sandwich?
::prev::
What is your favourite toy?
~`!##$%^& * ()_+=-{}[]|\"':;?/>.<,
What is your favourite toy?
::exit::
Exiting....
I apologize for it being long, I just want to cover all bases for the new game I'm going to be making. I want this to be the question to end all questions.
I want to be able to type ::prev:: wherever I am and have it go back to the previous question, and I'd like ::exit:: to exit the script wherever it is. Also, I'd like unrecognized input during questions with numbered responses to just reload the question without continuing, and for input containing characters that may cause a script break (something like :;!## ...) to reload the question instead of breaking.
Any help is greatly appreciated!
By the way, I'm using OS X Yosemite
First thing to do in this situation is to try and think of how, generally, you could implement something like this. Probably the biggest addition to complexity is using ::prev:: to go back a question. This means we need to represent the application state in some way such that we can move forward or backward.
Luckily, this is pretty simple: it's basically just an implementation of a stack that we need. Some visuals:
...
<--pop-- <--pop-- Location prompt <--pop-- ...
Name prompt Name prompt ...
Main menu --push-> Main menu --push-> Main menu --push-> ...
This also means each individual piece of the program needs to be self-contained. We can easily do this in shell scripting with functions.
So we need several pieces:
Function which displays a menu and allows the user to choose a value.
Function which displays a text prompt and allows the user to choose a value.
Function which manages a stack that represents the state of the program.
Individual functions for each piece of the program.
Let's first write our menu prompt function. This part is pretty easy. Bash will do most of the work using the select loop, which prints a menu for us. We'll just wrap it so that we can handle custom logic, like expecting ::exit:: or ::prev:: and some pretty-printing.
function show_menu {
echo "$1" # Print the prompt
PS3='> ' # Set prompt string 3 to '> '
select selection in "${menu_items[#]}" # Print menu using the menu_items array
do
if [[ "$REPLY" =~ ^(::exit::|::prev::)$ ]]; then
# If the user types ::exit:: or ::prev::, exit the select loop
# and return 1 from the function, with $selection containing
# the command the user entered.
selection="$REPLY"
return 1
fi
# $selection will be blank if the user did not choose a valid menu item.
if [ -z "$selection" ]; then
# Display error message if $selection is blank
echo 'Invalid input. Please choose an option from the menu.'
else
# Otherwise, return a success return code.
return 0
fi
done
}
We can now use this function like so:
menu_items=('Item 1' 'Item 2' 'Item 3')
if ! show_menu 'Please choose an item from the menu below.'; then
echo "You entered the command $selection."
fi
echo "You chose $selection."
Great! Now on to the next item on the agenda, writing the code that accepts text input from the user.
# Prompt for a required text value.
function prompt_req {
# do...while
while : ; do
# Print the prompt on one line, then '> ' on the next.
echo "$1"
printf '> '
read -r selection # Read user input into $selection
if [ -z "$selection" ]; then
# Show error if $selection is empty.
echo 'A value is required.'
continue
elif [[ "$selection" =~ ^(::exit::|::prev::)$ ]]; then
# Return non-success return code if ::exit:: or ::prev:: were entered.
return 1
elif [[ "$selection" =~ [^a-zA-Z0-9\'\ ] ]]; then
# Make sure input only contains a whitelist of allowed characters.
# If it has other characters, print an error and retry.
echo "Invalid characters in input. Allowed characters are a-z, A-Z, 0-9, ', and spaces."
continue
fi
# This break statement only runs if no issues were found with the input.
# Exits the while loop and the function returns a success return code.
break
done
}
Great. This function works similarly to the first:
if ! prompt_req 'Please enter a value.'; then
echo "You entered the command $selection."
fi
echo "You entered '$selection'."
Now that we have user input handled, we need to handle the program flow with our stack-managing function. This is fairly easy to implement in bash using an array.
When a part of the program runs and completes, it will ask the flow manager to run the next function. The flow manager will push the name of the next function onto stack, or rather, add it to the end of the array, and then run it. If ::prev:: is entered, it will pop the last function's name off of the stack, or remove the last element of the array, and then run the function before it.
Less talk, more code:
# Program flow manager
function run_funcs {
# Define our "stack" with its initial value being the function name
# passed directly to run_funcs
funcs=("$1")
# do...while
while : ; do
# Reset next_func
next_func=''
# Call the last function name in funcs.
if "${funcs[${#funcs[#]}-1]}"; then
# If the function returned 0, then no :: command was run by the user.
if [ -z "$next_func" ]; then
# If the function didn't set the next function to run, exit the program.
exit 0
else
# Otherwise, add the next function to run to the funcs array. (push)
funcs+=("$next_func")
fi
else
# If the function returned a value other than 0, a command was run.
# The exact command run will be in $selection
if [ "$selection" == "::prev::" ]; then
if [ ${#funcs[#]} -lt 2 ]; then
# If there is only 1 function in funcs, then we can't go back
# because there's no other function to call.
echo 'There is no previous screen to return to.'
else
# Remove the last function from funcs. (pop)
unset funcs[${#funcs[#]}-1]
fi
else
# If $selection isn't ::prev::, then it's ::exit::
exit 0
fi
fi
# Add a line break between function calls to keep the output clean.
echo
done
}
Our run_funcs function expects:
to be called with the name of the first function to run, and
that each function that runs will output the name of the next function to run to next_func if execution of the program must proceed.
Alright. That should be pretty simple to work with. Let's actually write the program now:
function main_menu {
menu_items=('Play' 'Load' 'Exit')
if ! show_menu 'Please choose from the menu below.'; then
return 1
fi
if [ "$selection" == 'Exit' ]; then
exit 0
fi
if [ "$selection" == 'Load' ]; then
# Logic to load game state
echo 'Game loaded.'
fi
next_func='prompt_name'
}
function prompt_name {
if ! prompt_req 'What is your name?'; then
return 1
fi
name="$selection"
next_func='prompt_location'
}
function prompt_location {
if ! prompt_req "Where are you from, $name?"; then
return 1
fi
location="$selection"
next_func='prompt_colour'
}
function prompt_colour {
menu_items=('Blue' 'Red' 'Green' 'Grey' 'Magenta')
if ! show_menu 'What is your favourite colour?'; then
return 1
fi
colour="$selection"
next_func='prompt_toy'
}
function prompt_toy {
if ! prompt_req 'What is your favourite toy?'; then
return 1
fi
toy="$selection"
next_func='print_player_info'
}
function print_player_info {
echo "Your name is $name."
echo "You are from $location."
echo "Your favourite colour is $colour."
echo "Your favourite toy is $toy."
# next_func is not set, so the program will exit after running this function.
}
echo 'My Bash Game'
echo
# Start the program, with main_menu as an entry point.
run_funcs main_menu
Everything is in order now. Let's try out our program!
$ ./bash_game.sh
My Bash Game
Please choose from the menu below.
1) Play
2) Load
3) Exit
> ::prev::
There is no previous screen to return to.
Please choose from the menu below.
1) Play
2) Load
3) Exit
> 2
Game loaded.
What is your name?
> Stephanie
Where are you from, Stephanie?
> ::prev::
What is your name?
> Samantha
Where are you from, Samantha?
> Dubl*n
Invalid characters in input. Allowed characters are a-z, A-Z, 0-9, ', and spaces.
Where are you from, Samantha?
> Dublin
What is your favourite colour?
1) Blue
2) Red
3) Green
4) Grey
5) Magenta
> Aquamarine
Invalid input. Please choose an option from the menu.
> 8
Invalid input. Please choose an option from the menu.
> 1
What is your favourite toy?
> Teddie bear
Your name is Samantha.
You are from Dublin.
Your favourite colour is Blue.
Your favourite toy is Teddie bear.
And there you have it.

How a function should exit when it called with here-document from shell script?

I have a COBOL program which should be run thru shell script and should accept the values from the here document. in the here document, i should call a function that should let control to be exit abnormally with exit code.
I have tried as below but it is not working for me.
This is my COBOL program:
01 WW-ANS PIC X value space.
IRS-200.
display "ARE THE ABOVE ANSWERS CORRECT? Y/N/E".
Accept ws-ans.
display "entered value is " ws-ans "<".
IF WW-ANS = "E" or "e"
PERFORM STOP-RUN-CA.
IF WW-ANS NOT = "Y" AND "N" AND "E"
and "y" and "n" and "e"
PERFORM DISPLAY-01 THRU DISPLAY-01-EXIT
GO TO IRS-200.
IF WW-ANS = "Y" or "y"
display "Program executed successfully"
PERFORM STOP-RUN-CA.
ELSE
GO TO IRS-200.
DISPLAY-01.
DISPLAY "value is >" WW-ANS "<".
DISPLAY "INVALID RESPONSE".
This is my shell script:
#!/bin/bash
funexit ()
{
echo "calling funexit"
exit 1
}
/caplus/pub/test123<<:EOD:
1
g
$(funexit)
Y
:EOD:
Output is:
[Linux Dev:adminusr ~]$ ./test123.sh
ARE THE ABOVE ANSWERS CORRECT? Y/N/E
entered value is 1<
value is >1<
INVALID RESPONSE
ARE THE ABOVE ANSWERS CORRECT? Y/N/E
entered value is g<
value is >G<
INVALID RESPONSE
ARE THE ABOVE ANSWERS CORRECT? Y/N/E
entered value is c<
value is >C<
INVALID RESPONSE
ARE THE ABOVE ANSWERS CORRECT? Y/N/E
entered value is Y<
Program executed successfully
When ever function gets called from here document the COBOL program accept the value as "C", since at function: it invoke the echo command and considering the first character from the "calling funexit" string, instead of getting exit.
From the function, I have removed echo statement like below:
#!/bin/bash
funexit ()
{
exit 1
}
/caplus/pub/test123<<:EOD:
1
g
$(funexit)
Y
:EOD:
Output is:
[Linux Dev:adminusr ~]$ ./test123.sh
ARE THE ABOVE ANSWERS CORRECT? Y/N/E
entered value is 1<
value is >1<
INVALID RESPONSE
ARE THE ABOVE ANSWERS CORRECT? Y/N/E
entered value is g<
value is >G<
INVALID RESPONSE
ARE THE ABOVE ANSWERS CORRECT? Y/N/E
entered value is <
value is > <
INVALID RESPONSE
ARE THE ABOVE ANSWERS CORRECT? Y/N/E
entered value is Y<
Program executed successfully.
When ever function gets called from here document the COBOL program accept the value as spaces instead of getting exit.
The script should get exit abnormally with some exit code.
This example looks contrived because you are giving it both static input. However, that is probably because this is just a simplified example.
I guess is that you want funexit to return the E so that the input would mean that there is no more input, and nothing if there is more input. To be clear, the funexit script is called (and finishes) before COBOL is called.
I think you would want to code your shell script like this:
#!/bin/bash
funexit ()
{
echo calling funexit 1>&2
echo E
}
/caplus/pub/test123<<:EOD:
1
g
$(funexit)Y
:EOD:
Notice that the call to funexit and the following Y are on the same line. That way, if funexit did not return anything, the cobol program would not see a blank line. If funexit returns an E (actually an E followed by a new line) it would see the E.
Also notice that your debugging output of "Calling funexit" is redirected to standard error; before it was being sent to the cobol program and it ACCEPTed the letter C (from "Calling").
And lastly, the funexit script does not need to exit, as that is what will happen at the end of the script anyway.
#ANR;
Use ACCEPT ... ON EXCEPTION
Ala
identification division.
program-id. sample.
data division.
working-storage section.
01 the-fields.
05 field-one pic x(8).
05 field-two pic x(8).
05 field-three pic x(8).
05 field-four pic x(8).
*> ***************************************************************
procedure division.
accept field-one end-accept
display field-one end-display
accept field-two end-accept
display field-two end-display
accept field-three
on exception
display "no field three entered" end-display
not on exception
display field-three end-display
end-accept
accept field-four
on exception
display "no field four entered" end-display
not on exception
display field-four end-display
end-accept
goback.
end program sample.
So a run with four lines of input looks like
./sample <fourdatums.txt
one
two
three
four
and with only three
./sample <threedatums.txt
one
two
three
no field four entered
Since funexit is executed in a subshell created by the command substitution, you'll need to check its exit status outside the here document to determine if the parent shell should exit.
#!/bin/bash
funexit () {
echo "calling funexit"
exit 1
}
output=$(funexit) || exit
/caplus/pub/test123<<:EOD:
1
g
$output
Y
:EOD:
Assign the output of your function to a variable outside the heredoc and check for failure there:
#!/bin/bash
funexit ()
{
exit 1
}
if ! funexitvalue=$(funexit) ; then
echo "Failure!"
exit 1
fi
/caplus/pub/test123<<:EOD:
1
g
$funexitvalue
Y
:EOD:
This will prevent the COBOL program to be run if funexit does not exit successfully.
Explanation:
Command substitutions ($(...)) in bash always "work", as in, the substitution is replaced by the output of the command. Even if the command exits with an error:
$ echo "$(echo foobar; exit 1)"
foobar
$ echo $?
0
As you can see, the first echo exits successfully, even though the command in the substitution failed. The return code of the command substitution does not affect the actual command in any way.
In contrast:
$ a="$(echo foobar; exit 1)"
$ echo $?
1
$ echo $a
foobar
Here the return code of the command substitution is not shadowed by any other command, so you can actually check, if it returned successfully. Anyway, the assignment of the output to a was still successful.
Note: If you intended the COBOL program to be run up to the point where the faulty input would happen, than heredocs are not the way to go, as they are completely evaluated before they are even passed to the program. So with heredocs it is an all or nothing deal.
The heredoc is going to be parsed by the shell and passed as input in its entirety, and your approach is not going to work. However, you can certainly break up the input:
{ cat << EOF
This text will always be entered to the test123 executable;
EOF
funexit # exit or return, as needed
cat << EOF
If funexit returned, this will go to test123.
If funexit exited, it will not
EOF
} | caplus/pub/test123

./test: line 22: syntax error near unexpected token `done'

#!/bin/bash
# script to create new instance by taking inputs from user
source /root/keystonerc_admin
unset http_proxy
function main {
echo "Please choose:
1. Create instance from an image
2. Create instance from a volume
3. Exit"
While true do
read SELECT
case "$SELECT" in
1) SELECT=func_create_from_image;;
2) SELECT=func_create_from_volume;;
3) SELECT=exit;;
*) echo Invalid selection.; continue
esac
break
done
}
function func_create_from_image {
echo "List of flavors"
nova flavor-list
echo "Enter flavor id from the list"
read flavor_id
echo "List of images"
nova image-list
echo "Enter image id from the list"
read image_id
echo "List of security groups"
nova secgroup-list --all-tenants
echo "Enter security group name from the list"
read secgroup_name
echo "List of keypairs"
nova keypair-list
echo "Enter keypair name from the list"
read keypair_id
echo "Enter display name of instance"
read display_name
## use "set" for debugging
##set -x
nova boot --display $display_name --flavor $flavor_id --image $image_id \
--key_name $keypair_id --security_group $secgroup_name
##set +x
}
function func_create_from_volume {
echo "Please choose:
1. Use existing bootable volume
2. Create new bootable volume
3. Exit"
while true do
read SELECT
case "$SELECT" in
1) SELECT=create_instance_from__existing_volume;;
2) SELECT=create_instance_from_new_volume;;
3) SELECT=exit;;
*) echo Invalid selection.; continue
esac
break
done
}
create_instance_from_new_volume {
echo "Specify image id from the below list"
nova image-list
read image_id
echo "specify size in GB, only specify number"
read vol_size
echo "Specify image name to be displayed in cinder list"
read vol_disp_name
set -x
cinder create --image-id $image_id --display-name $vol_disp_name $vol_size
set +x
echo "volume created"
cinder list
echo "create instance"
create_instance_from__existing_volume
}
create_instance_from_existing_volume {
echo "Specify volume id from the below list"
cinder list
read vol_id
echo "Pick flavor id from the below list"
nova flavor-list
read flavor_id
echo "specify size in GB, only specify number or leave blank for system to decide"
read vol_size
echo "Specify type of volume either snap or other or leave blank if not known"
read vol_type
echo "Specify 1 if volume should be deleted when the instance terminates or specify 2 if volume remains after
instance terminates"
read vol_del
if [$vol_del!=0 or $vol_del!=1] then
echo "Specify either 1 or 0"
fi
echo "Specify display name for instance"
read inst_disp_name
echo "Creating instance"
nova boot --flavor $flavor_id --block_device_mapping vda=$vol_id:$vol_type:$vol_size:$vol_del $inst_disp_name
}
While true do
Bash is case-sensitive - you need while rather than While. And you also need to either move the do to the next line or put a semicolon before it
while true; do
Also your if condition further down is suspect:
if [$vol_del!=0 or $vol_del!=1] then
Aside from the syntax errors, this condition will always be true (since if the value is 1 then it is not 0 and vice versa). I suspect you wanted and rather than or:
if [ $vol_del != 0 -a $vol_del != 1 ]; then
Use
while true; do
instead of
While true do
because do isn't an argument to true command
In theory, do should be on a separate line:
while ....
do
....
done
Same with then:
if .....
then
.....
fi
However, people like putting them on the same line in the K&R Style. To do that, you need a semicolon to show the shell when the while line ends, so the do is on a different line:
while ....; do
....
done
This may no longer be necessary in newer versions of BASH, but it should be done. I prefer using an extra line just because the shell prefers it.
The other problem and the actual problem has to do with the While on line #13 as While (uppercase W and not lowercase w).
Two thing will help catch this in the future:
Use set -xv to turn on debugging and set +xv to turn off debugging. In this case, you'll see the error happens when you enter the while loop when Line #13 gets executed. You can set export PS4=\$LINENO+ to show line numbers when this debugging is on. To turn off debugging, you set +xv.
Use a program editor that has syntax highlighting. I saw this immediately because the While wasn't in the correct color. I took a closer look and saw the capitalized While.

How to print and edit variable content at the same place on runtime of a Unix shell script?

I have been looking all around (with no avail) to perform the following:
I want to display and able to edit if necessary the content of a variable in runtime for a Unix shell script after giving it a value. The idea goes like this:
Suppose we have a variable value either defined in script or by user input
var=12345
Print the variable value, but also leave the cursor in that position of printing and either press just enter to leave it intact or enter a new value in runtime
Edit variable content (press Enter to leave intact) : 12345
at this point at runtime, I want to leave the cursor in the position of number 1, while showing the variable, and working in the way that if I press Enter, leave the original content (12345) or read for a new value in that same place and modify. Clearing the display of the variable when typing anything else than Enter would be a big plus.
I have looked all around for a way to do this, but just haven't found anything. Anyone willing to provide a solution?
I would suggest you doing this in another way:
var=12345
echo "Change the value of Var (current value: $var)"
read -p "New value (Press Enter to skip):" nvar
if [[ "$nvar" != "" ]]; then
var="$nvar"
fi
echo $var
with this, it will prompt:
Change the value of Var (current value: 12345)
New value (Press Enter to skip):
what you wanted in the question (without the "big plus" part), could be achieved too:
var=12345
echo -ne "Now you may want to change the value: \n$var\r"
read nvar
if [[ "$nvar" != "" ]]; then
var="$nvar"
fi
echo $var
with this, it will prompt (cursor sits between ** )
Now you may want to change the value:
*1*2345
With bash, you could use the readline option for read, but that does not put the cursor at the beginning. It does however do what you want
var=12345
read -ep "Enter new value: " -i $var var
If you need the cursor to go back, you can do this:
var=12345
prompt="Enter new value: $var"
for ((i=1; i<=${#var}; i++)); do prompt+=$'\b'; done
read -p "$prompt" new
[[ -z $new ]] && new=$var

Resources