Bash menu opens and closes

Ever since I added these two code blocks to my bash menu it just opens and closes right away. I use a batch file that worked fine until these codes were added and I am not sure what is wrong.

Basically, what I am trying to do in the additional section is if the answer is "Y" then it goes back to the first command (gjb2), but if the answer is "N" then it goes to the annovar section.

After annovar runs the user is prompted again and if the answer is "Y" then it goes back to the menu, but if the answer is "N" then the programs exits. Thank you :).

I attached the full .sh as well. Thank you :slight_smile:

 additional() {
    printf "\n\n"
    printf "Are there additonal GJB2 patients to be analyzed?  Y/N "; read match_choice

    case "$match_choice" in
        [yY]) id=""; gjb2 ;;
        [nN]) id=""; annovar ;;  
    esac
}

annovar() {
    printf "\n\n"
    printf "How many patients  : "; read id

    [ -z "$id" ] && printf "\n No ID supplied. Leaving match function." && sleep 2 && menu
    [ "$id" = "end" ] && printf "\n Leaving match function." && sleep 2 && menu

    cd 'C:\Users\cmccabe\Desktop\annovar'
              $( perl -ne 'chomp; system ("perl table_annovar.pl $_ humandb/ -buildver hg19 -protocol refGene,popfreq_all,common,clinvar,clinvarsubmit,clinvarreference -operation g,f,f,f,f,f -otherinfo")' < sanger.txt )
     printf "The annotation is complete, would you like analyze additional patients  : "; read match_choice
	 case "$match_choice" in
        [yY]) id=""; menu ;;
        [nN]) id=""; printf "\n Goodbye! \n\n"; exit ;;
}
# actual start of this program
menu # run menu function 

Looks like an esac is missing in the annovar function...

1 Like

Thank you :), it is working now.

---------- Post updated at 09:24 AM ---------- Previous update was at 08:08 AM ----------

Spoke to soon, I added the below lines and the same thing happens, can you please take a look at what I am over-looking? Thank you :).

What is supposed to happen is after the variant is entered by the user it is saved in ${id}.txt and the NameChecker python script is run.
The result of the NameChecker is displayed and the user is asked if the variant is correct, if the answer is "Y" then goto the position function, but if the answer is "N" then goto the gjb2 function and ${id}.txt is cleared. Thank you :).

 # run python NameChecker
	cd 'C:'
    C:/Users/cmccabe/Desktop/Python27/python.exe C:/Users/cmccabe/Desktop/Python27/run_batch_job.py C:/Users/cmccabe/Desktop/Python27/${id}.txt NameChecker
	
check() {
    printf "\n\n"
    printf "Is the variant correct?  Y/N "; read match_choice

    case "$match_choice" in
        [yY]) id=""; position ;;
        [nN]) id=""; gjb2 ;; echo -n "" > ${id}.txt  
    esac
}

    position() {                  
# run python PositionConverter
    printf "\n\n"
	cd 'C:'
    C:/Users/cmccabe/Desktop/Python27/python.exe C:/Users/cmccabe/Desktop/Python27/run_batch_job.py C:/Users/cmccabe/Desktop/Python27/${id}.txt C:/Users/cmccabe/Desktop/annovar/${id}.txt PositionConverter
    convert
} 

is ${id} set somewhere outside of the posted code?

Otherwise i dont recall windows shell that good, but i thought to change the disk, you would just enter c: , where to change to C: 's root dir, you would do a cd c:\ .
Saying, doing cd C: while beeing (anywhere) on C:, is about the same as in linux cd .

hth

Yes, ${id}.txt is set using the below code which is just outside:

Things worked before I added the new code, but I'm not sure what changed, I will start investigating the directory. Thank you :).

 gjb2() {
    printf "\n\n"
    printf "What is the id of the patient getting GJB2 analysis  : "; read id
    printf "Enter variant: "; read variant
 
 [ -z "$id" ] && printf "\n No ID supplied. Leaving match function." && sleep 2 && return
    [ "$id" = "end" ] && printf "\n Leaving match function." && sleep 2 && return
 
 # save file in windows python directory
    printf "%s\n" "$variant" > c:/Users/cmccabe/Desktop/Python27/$id.txt
 
 # add transcript
 sed -i '$a NM_004004.5:' c:/Users/cmccabe/Desktop/Python27/$id.txt
    printf "NM_004004.5:%s\n" "$variant" > c:/Users/cmccabe/Desktop/Python27/$id.txt 

You clear id and then echo > ${id}.txt ? That will create an empty .txt file...

In general, you do nested / recursive calls to functions (like calling menu from within a function that itself is called by menu). This is OK if you know exactly what you do and if you have a "conditional break" - the base case - in there somewhere. But that seems not to be the case, so I'd propose you take a step back, scribble a layout of your functions and respective function calls onto a scrap sheet and then try to implement that.

So I tried to map it out and explain the steps as well. The script is attached as well and it seems to work for the most part except for the two sections I added. I apologize in advance if I posted the text incorrectly I tried to follow a similar post. Thank you :).

menu -> gjb2 -> name -> check -> position -> parse -> add2text -> additional? -> annovar -> menu or message and exit
^       ^
|		|               
|       |                                             
user    mecp2 -> name -> check -> position -> parse -> add2text -> additional? -> annovar -> menu or message and exit
choice  ^
		|
		|
	    phox2b -> name -> check -> position -> parse -> add2text -> additional? -> annovar -> menu or message and exit
        ^
		|
		|
		+----------------------------------------------------------------------------------------------------------+
		^
		|
		|
		exit 
menu choice - user is prompted for selection (4 choices)	
selection - each selection has unique transcript assigned
name - python script to verify name syntax
check - user prompted to verify naming correct (Y/N) - after python script is run results of that appear on screen, so that the prompt can be answered
        if the answer is "Y" then next function (position), if "n" then return to function gjb2 and ${id}.txt cleared
position - python script to transfer ${id}.txt to annovar directory and convert to coordinates
parse - parse specific field to create ${id}_parse.txt
add2text - ${id}.txt is added to sanger.txt
additional - user is asked for additional data (Y/N) - if "Y" then goto to gjb2 function, if "N" then goto annovar function
annovar - perl script run on ${id}_parse.txt
after the script is run the user is told its completed and asked if there is more to do(Y/N) - if "Y" then menu function, if "N" then message and exit 

Wouldn't sth. like this make more sense (just an idea):

user choice 
|
v 
menu +> gjb2 -> name -> check -> position -> parse -> add2text -> additional? -> annovar -------+
  ^  |    ^                                                            v                        |
  |  |    +<---------------------------------------------------------<-+                        |
  |  |                                                                                          |
  |  +> mecp2 -> name -> check -> position -> parse -> add2text -> additional? -> annovar ------+
  |  |    ^                                                            v                        |
  |  |    +<---------------------------------------------------------<-+                        |
  |  |                                                                                          |
  |  +> phox2b -> name -> check -> position -> parse -> add2text -> additional? -> annovar -----+
  |  |    ^                                                            v                        |
  |  |    +<---------------------------------------------------------<-+                        |
  |  |                                                                                          |
  |  +> exit                                                                                    |
  +---------------------------------------------------------------------------------------------+

And it looks like there's quite some more potential to optimize.

The 'gjb2' function as no closing bracket } .
And similar to RudiC, i'd highly recomend that the default behaviour of funtions should be return .

However, the menu function should be the only function calling others, which already is.
Also use either break or return to exit a case statement within a function.

As you already have split up the functionality to functions.
It is recomended to 'list' the functions, rather than calling 1 function which calls the other which calls another, which calls another - as one might produce unexpected behaviour.

For better readability, try to always use the same indention-blocks.
Usualy thats either 4 or 8 spaces or tabs.

hth

---------- Post updated at 22:53 ---------- Previous update was at 22:40 ----------
Since the follow up handling for all the 3 functions is identical, i'd attempt something like:

user choice 
|
|         +<--------------------------------------------------------------\
v         V                                                                \
menu +> gjb2  ->  \                                                         \
  ^  |             \                                                         \
  |  |              \                                                         \
  |  |               \                                                         ^
  |  +> mecp2 -------- +-> name -> check -> position -> parse -> add2text -> additional? -> annovar ------+
  |  |    ^           /                                                        v                          |
  |  |    +<-----------------------------------------------------------------<-+                          |
  |  |              /                                                         /                           |
  |  +> phox2b ->  /                                                         /                            |
  |  |    ^                                                                 /                             |
  |  |    +----------------------------------------------------------------/                              |
  |  |                                                                                                    |
  |  +> exit                                                                                              |
  +-------------------------------------------------------------------------------------------------------+

As on grab the data from one of the selected functions, and pass it to the follow up functions, which seem to be identical for each of the selectables...

hth

The 3 user choices are virtually the same except for the sed which applies the correct transcript with the user choice. For example, if the choice is GJB2 then the NM_ is different then if the user choice was MECP2. I am out of my element here and am needing some expert assistance in making this code work. I am trying to learn all the terms, but admittedly slower it seems. Thank you :).

Make those 2 tasks, each a single function that works independant of each other.
Actualy you've already done so :wink:

Its all about the maintask function...

#!/usr/bin/env bash
#
#
# Functions
#
	function XY...
	...
	...
	
	function menu() {
	# Shows the menu and loops until Exit has been selected
	while true
		do
			printf "\n Welcome to annovar, please make a selection from the MENU \n
			==================================\n\n
			\t 1  GJB2 analysis\n
			\t 2  MECP2 analysis\n
			\t 3  Phox2B analysis\n
			\t 4  Exit\n\n
			==================================\n\n"

			printf "\t Your choice: "; read menu_choice

			case "$menu_choice" in
			1) maintask gjb2 ;;
			2) maintask mecp2 ;;
			3) maintask phox2b ;;
			4) printf "\n Bye! \n\n"; return 0 ;;
			*) printf "\n Invalid choice."; sleep 2 ;;
			esac
		done
	}
	function maintask() {
	# First and only argument expected to be the functions name!
		loop=true
		while $loop
		do
			$1       ### This would be one of:: gjb2 mecp2 phox2b
			check || break
			position
			convert
			add2text
			read -n1 -p "Do an additional patient for $1?"  yesno
			[ "$yesno" = y ] || loop=false
		done
		annovar
	}
#
# Action & Display
#
	menu

Hope this helps

Thank you for the tips and help (very useful)... for the check function a file is created in C:/Users/cmccabe/Desktop/annovar/${id}.txt (attached), that has a warning column in it at $2 . If the variant has an error message in it can that be displayed as part of the check function? And if that field is blank "no errors" is displayed?

Also, in the echo -n what is the correct syntax to clear two or three files? Thank you :slight_smile:

 check() {
    printf "\n\n"
    " warning from ${id}.txt in annovar directory"
    printf "Is the variant correct?  Y/N "; read match_choice

    case "$match_choice" in
        [yY]) id=""; position ;;
        [nN]) id=""; echo -n "" > C:/Users/cmccabe/Desktop/Python27/${id}.txt ; gjb2 ;;  
    esac
} 
    printf "\n\n"
    " warning from ${id}.txt in annovar directory"

Try:

    printf "\n\nwarning from ${id}.txt in annovar directory"

Just seeing i'm running out of time in RL...
I'm not good at awk yet, but maybe this could help to display the error message if one exists.

awk '
	NR==1
	if($2 != ""){
		print $2
	}'C:/Users/cmccabe/Desktop/Python27/${id}.txt

hth

1 Like

The command to create a regular file if it did not already exist or to truncate it to size zero if does already exist is:

> pathname

For more than one file, the syntax is:

> pathname1
> pathname2
...
> pathnameN

If the pathnames contain directories as well as a filename, that/those directory/directories must already exist for this to work. If you don't know if there might be missing directories, you can use:

mkdir -p "$(dirname pathname)" && > pathname
1 Like