Link to home
Start Free TrialLog in
Avatar of Jofnn
JofnnFlag for United Kingdom of Great Britain and Northern Ireland

asked on

Batch file to build menu's from an INI file

I've accidentally assigned this to "outlook"... it should be in "batch" by default!!!

Hi,

We have a batch script that extracts results in an INI file to determine it's variables whilst running... I make it do this by using:
FOR /f "tokens=1* delims== " %%d in ('Type %ini_file% ^| find %certain_key%') do set answer=%%e

Open in new window


This all works fine for getting information for set keys... however, now I'm trying to get the script to build a menu based on what it finds... (i.e. the app menu's will be detailed in the INI file).  So - for example - the ini file would look like:

menuoption1=Apple
menuoption2=Banana
menuoption3=Pear

Open in new window


I COULD use my above line to actually read the set lines... however, the INI file may be amended at any time to add any number of additional options (say for example menuoption100).  How can I get the line of code to continue searching for the value "menuoption" and keep going through each number until it can't find the next one?

So... Script runs... references the ini file and goes through menuoption1, menuoption2, menuoption3 and keeps going till it notices that the next number isn't there... so menuoption101 isn't on there, so it stops?

These need to have a variable set specifically for each variable as they will be referenced later.

Any help appreciated!


Avatar of Bill Prew
Bill Prew

See if this gives you some ideas.  I read the INI file, and pick out only the lines starting with "menuoption".

Then I do 2 things, not knowing how best you might want to use the options.  First, we set a variable from the line in the INI file, so variable "menuoption1" picks up "Apple", etc.  In addition, I build a concatenated list as each new option is read, in a single variable.  This allows FOR looping over that list easily, as demonstrated in the attached sample.  

Hopefully this gives you some ideas, let me know if you have questions.

~bp
@echo off
setlocal EnableDelayedExpansion
 
REM Define location of INI file
set ini_file=c:\temp\EE26458355.ini
 
REM Clear variable to hold concatenated list of all menuoptions
set MenuList=
 
REM Read each line if INI file looking for menuoptions
for /F "usebackq tokens=1* delims==" %%A in ("%ini_file%") do (
  set Name=%%~A
  if /I "!Name:~0,10!" == "menuoption" (
    set %%~A=%%~B
    if !MenuList! == "" (
      set MenuList="%%~B"
    ) else (
      set MenuList=!MenuList!,"%%~B"
    )
  )
)
 
ECHO Display of all set variables named menuoption*
set menuoption
 
ECHO Looping through menu elements
for %%A in (%MenuList%) do (
  echo %%~A
)

Open in new window

Avatar of Jofnn

ASKER

Thanks BP,

I'll have a play around with that and let you know... Looks good though!

Thanks :o)
Jonathon
Avatar of Jofnn

ASKER

Thanks - I've had a play around now, but I'm having "some trouble".

Once the script has checked the ini, each of those options should have it's own variable (like O1,O2) so that the user can type it in and press enter... and it'll match it and perform the relevant action...

I.e. you select option O6 and it will run a certain command based on what the option in the ini was ...

make sense?
No, I don't get what you are trying to accomplish yet.  After my code ran, each entry in the INI file will have it's own variable called MENUOPTION1, MENUOPTION2, etc.  Those variables have the value of the corresponding line in the INI file.  I think you then want to present that list of items on the screen to the user, and let them select the one they want by entering it's number.  But then how does the BAT file know what command to execute for that particular menu item?

~bp
Avatar of Jofnn

ASKER

Bill,

The plan for the script is to follow this sort of process:

  -  User launches script
  -  Script reads INI file to locate relevant options defined in the INI file
  -  User is shown a list of the available options on screen shown as (1) Option1, (2) Option 2,  (3) Option3 etc
  -  The user will type in the option (e.g. 2) and press enter

Once they have done this, the script will read the data stored under that number (which will actually be a filename).  Dependant on the last 3 letters, depends what happens next (i.e. .vbs will launch a VBS section... .doc will launch a DOC section etc).

Does that make more sense?
Jonathon
Just a quick question Bill.  Depending on what version of Windows is used, doesn't it sound like a good use of the "choice" command here?
So can you give me a more meaningful sample INI?  Based on what you are saying, I'm thinking it looks like this:

menuoption1=c:\temp\test.bat
menuoption2=c:\temp\test.vbs
menuoption3=c:\other\foo.bat

But what do we display to the user when we put up the menu so they know what each choice represents?

~bp
Bill,

Not to intrude but I think something like this is what the author is after. I can't complete it or test it since I am running XP Pro and "choice" doesn't work on XP but I think it might explain it a little better.  :(
@echo off
setlocal EnableDelayedExpansion
 
REM Define location of INI file
set ini_file=c:\test\EE26458355.ini
 
REM Clear variable to hold concatenated list of all menuoptions
set MenuList=
 
REM Read each line if INI file looking for menuoptions
for /F "usebackq tokens=1* delims==" %%A in ("%ini_file%") do (
  set Name=%%~A
  if /I "!Name:~0,10!" == "menuoption" (
    set %%~A=%%~B
    if !MenuList! == "" (
      set MenuList="%%~B"
    ) else (
      set MenuList=!MenuList!,"%%~B"
    )
  )
)
 
ECHO Display of all set variables named menuoption*
set menuoption

:main 
ECHO Looping through menu elements
set /a count=0
for %%A in (%MenuList%) do (
  set /a count=!count!+1
  echo !count! %%~A  
)

choice Your choice: /c:12345
if errorlevel 5 goto bad1
if errorlevel 4 goto exit
if errorlevel 3 goto exit
if errorlevel 2 goto exit
if errorlevel 1 goto exit


:bad1
echo invalid choice - please try again
goto main

:exit
echo still need to build the rest  :)

Open in new window

Oh yea, I changed the directory on line 5 from temp to test as that is what I use.
Avatar of Jofnn

ASKER

@billprew - apologies for the response, it's been a bit hectic over here!  

The ini file is going to be constructed in one of two ways (still to be decided how to go)...

[OPTION A -- where visible name and location are split by comma]
menuoption1=catVBS,c:\temp\cat.vbs
menuoption2=dogBAT,c:\temp\dog.bat
menuoption3=eelREG,c:\temp\eel.reg

[OPTION B -- where visible name and location are defined separately]
menuoption1_name=catVBS
menuoption1_loca=c:\temp\cat.vbs
menuoption2_name=dogBAT
menuoption2_loca=c:\temp\dog.bat

We haven't decided on the final layout of the ini file, but we would manipulate the code accordingly!

Hope that makes sense
Avatar of Jofnn

ASKER

@pony10us

Choice doesn't seem to be working?  Is this a POST XP command?
==> Jofnn

Okay, that's the missing piece.  I'll put something together for one or both of those options and you can see how it looks.

~bp
Avatar of Jofnn

ASKER

@billprew -- thank you very much :)

@pony10us -- ignore my last comment to you... just re-read the post above the code and realised you've already confirmed about the choice part!
Try this instead.  SET /P is more reliable than Choice as long as you are running Win 2K or better (Such as XP)

@ECHO off
 
:: SET Variablea here
SET "SrcFile=c:\test\EE26458355.ini"
SET "Count=0"
SET "Msg=GOOD"
:: Read Menu File and populate Options:

FOR /F "Tokens=1-3 delims==," %%O IN ('Type "%SrcFile%"') DO SET "%%OName=%%P"&SET "%%OFile=%%Q"&CALL :Start-List-Count


::Display Menu
:Start-Build-Menu
CLS
ECHO.
ECHO ---------------------------------------------------------------------------
ECHO  Please select one of the following choices fromt he list below:
ECHO ---------------------------------------------------------------------------
ECHO.
FOR /L %%L IN (1,1,%Count%) DO CALL ECHO Press %%L for %%MenuItem%%LName%%
IF /I "%Msg%"=="BAD" ECHO. NOTE: "%Choice%" Is NOT a Valid Option!
ECHO.
SET /P Choice=Please type the Number of your Choice (-1 to Exit): 
SET /A TChc=0+%Choice%
SET "Msg=GOOD"
IF /I "%TChc%"=="-1" GOTO :EOF
IF /I %TChc% Gtr %Count% SET "Msg=BAD"&GOTO Start-Build-Menu
IF /I %TChc% Lss 1 SET "Msg=BAD"&GOTO Start-Build-Menu
:End-Build-Menu


:: Run Command
IF /I "%Msg%" NEQ "BAD" CALL SET "RunThis=%%MenuItem%%LName%%"
IF /I "%Msg%" NEQ "BAD"ECHO Now Running "%RunThis%"
CALL %RunThis%
pause
GOTO :EOF

GOTO End-List-Count
:Start-List-Count
SET /A Count=Count+1
:End-List-Count

Open in new window

Thank you Q.  I just got in and read this and was going to rewrite the code with the SET /P. I am never sure which way to write it so maybe I should start putting in a check for OS and branch to the proper code so that it would work regardless.   :)
@Pony,

NP-- If you aren't using any operating systems older than Windows 2000 I suggest just sticking with SET /P for your scripting.

  I say this because the change to include the addition of SET /P was implemented in Windows 2000 but Choice began having implementation problems around the same time.

  Since I code almost exclusively for NT5+ I always use SET/P

If you need to code for Win 9x, or NT4- then you would want to use Choice on those systems and then on those systems it woudl definitely be worth your while to be checking Windows Version.

  I actually have some code to do this... if you wanna open a Question and link me I'll add it (What can I say I'm a points whore, and a solution whore, I would rather that someone else looking for said code, find it, instead of it being posted on another author's Q where it'll likely never be found by someone who needs it.

(Though, the code is untested on many of the OSs it checks for so there are likely bugs which woudl need to be ironed out..)
Q

Sounds good.  I don't often find OS's older than 2000 but every once in awhile I do. I have some mixed up code that I use so it would be interesting to see what you have.  Please see my open question: 26469806.

Thank you.
Avatar of Jofnn

ASKER

Hi QCubed,

I'm having a tinker about with this at the moment... but it's looking good so far!

Jonathon
Avatar of Jofnn

ASKER

Hi again,

I've put that in and amended the ini file to use the "," option rather than the additional key... The script works fine up until it creates the menu...

It creates the right amount of "Press x for" lines, but doesn't actually give a menu option to mirror the information in the INI.  Turned ECHO back on and slowly stepped through each stage... and I can see that in the initial FOR loop, it is pulling through the correct information at the time... but it won't build the menu.

I'm working on it to see if I can move it around a little - but thought I'd give you an update :)
Try adding line 2 from Bill and my initial code:   setlocal EnableDelayedExpansion
and see if that resolves it.
@Jofhn  The issue was that I had used "Menu Item" while your ini file uses "MenuOption" I fixed it touse "Menu Option"  also, I accidentally tried to call the name instead of the path.  this is the peril of trying to do this without testing sorry!!

-B

@ECHO off
 
:: SET Variablea here
SET "SrcFile=c:\test\EE26458355.ini"
SET "Count=-1"
SET "Msg=GOOD"
:: Read Menu File and populate Options:

FOR /F "Tokens=1-3 delims==," %%O IN ('Type "%SrcFile%"') DO SET "%%OName=%%P"&SET "%%OFile=%%Q"&CALL :Start-List-Count


::Display Menu
:Start-Build-Menu
CLS
ECHO.
ECHO ---------------------------------------------------------------------------
ECHO  Please select one of the following choices fromt he list below:
ECHO ---------------------------------------------------------------------------
ECHO.
FOR /L %%L IN (1,1,%Count%) DO CALL ECHO Press %%L for %%MenuOption%%LName%%
IF /I "%Msg%"=="BAD" ECHO. NOTE: "%Choice%" Is NOT a Valid Option!
ECHO.
SET /P Choice=Please type the Number of your Choice (-1 to Exit): 
SET /A TChc=0+%Choice%
SET "Msg=GOOD"
IF /I "%TChc%"=="-1" GOTO :EOF
IF /I %TChc% Gtr %Count% SET "Msg=BAD"&GOTO Start-Build-Menu
IF /I %TChc% Lss 1 SET "Msg=BAD"&GOTO Start-Build-Menu
:End-Build-Menu


:: Run Command
IF /I "%Msg%" NEQ "BAD" CALL SET "RunThis=%%MenuOption%TChc%File%%"
IF /I "%Msg%" NEQ "BAD" ECHO Now Running "%RunThis%"
CALL %RunThis%
pause
GOTO :EOF

GOTO End-List-Count
:Start-List-Count
SET /A Count=Count+1
:End-List-Count

Open in new window

SOLUTION
Avatar of Ben Personick (Previously QCubed)
Ben Personick (Previously QCubed)
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Jofnn

ASKER

Thanks Guys,

I'll have a play around with that tomorrow when back in the office :o)

Jonathon
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Jofnn

ASKER

Thanks Bill,

I'll have a play around with that (and QCubed's version) this morning and come back to you both!

Thanks again for your help :)

Jonathon
Avatar of Jofnn

ASKER

Right - I've had a play around... Personally, I prefer Bill's logic and think I'll use this.  I will use parts from both to be honest as they are exactly what I'm looking for!

I have one question though for you Bill...

Why does the 1st option in the INI file return with ""'s round it but none of the others?  I've had a look and can't see why it'd do it for one of them only... I've double checked the INI file and it's definately not in there with ""'s round it!

Jonathon
@Jofnn Bill put logic in that added quotes on the first iteration only.  I take it you Don't want the quotes.  this post is Bill's code edited not to put any quotes in at all.

  However he probably did this so as to ensure things were handled correctly if you have spaces.  so in the next post I will change his code to place quotes around every term when reading them, and then strip them off when displaying them or running them.

@echo off
setlocal EnableDelayedExpansion

REM Define location of INI file
set ini_file=c:\temp\EE26458355\EE26458355.ini

REM Make sure INI file exists
if not exist "%ini_file%" (
  echo Missing INI file ["%ini_file%"], stopping.
  exit /b
)

REM Clear variable to hold concatenated list of all menuo info
set MenuName=
set MenuLoca=

REM Read each line if INI file looking for menuoptions
for /F "usebackq tokens=1-3* delims=,=" %%A in ("%ini_file%") do (
  set Name=%%~A
  if /I "!Name:~0,10!" EQU "menuoption" (
    if "!MenuName!" EQU "" (set "MenuName=%%~B") else (set "MenuName=!MenuName!,%%~B")
    if "!MenuLoca!" EQU "" (set "MenuLoca=%%~C") else (set "MenuLoca=!MenuLoca!,%%~C")
  )
)

REM Display menu of choices
set /a i=0
for %%A in (%MenuName%) do (
  set /a i+=1
  echo.   [!i!] - %%A
)

REM Prompt them for a selection
set /P "Choice=Please Select one of the above options (1-%i%): "
if "%Choice%" NEQ "" if %Choice% GEQ 1 if %Choice% LEQ %i% goto :ProcessChoice
echo Invalid choice slected.
exit /b

REM Process their menu choice
:ProcessChoice
for /F "tokens=%Choice% delims=," %%A in ("%MenuName%") do set Name=%%A
for /F "tokens=%Choice% delims=," %%A in ("%MenuLoca%") do set Loca=%%A
echo Choice: %Choice%
echo Name: %Name%
echo Loca: %Loca%
REM ... rest of script logic goes here ...
exit /b

Open in new window

also note you will probably want to use my "SET /A Choice=0+%Choice% " command on the choice before you evaluate it.

I do this because Bills code will generate an error and skip the rest of the line where he evaluates the choice presented if the choice has any odd number of Quote symbols (") in it.

 but that line of code I used ensures that characters and odd " will be evaluated as 0

still I did not do anything to Bills code here other than to set every value read to be wrapped in quotes in case you use spaces, and then strip them off when printing the lines or running the commands




@echo off
setlocal EnableDelayedExpansion

REM Define location of INI file
set ini_file=c:\temp\EE26458355\EE26458355.ini

REM Make sure INI file exists
if not exist "%ini_file%" (
  echo Missing INI file ["%ini_file%"], stopping.
  exit /b
)

REM Clear variable to hold concatenated list of all menuo info
set MenuName=
set MenuLoca=

REM Read each line if INI file looking for menuoptions
for /F "usebackq tokens=1-3* delims=,=" %%A in ("%ini_file%") do (
  set Name=%%~A
  if /I "!Name:~0,10!" EQU "menuoption" (
    if "!MenuName!" EQU "" (set "MenuName="%%~B"") else (set "MenuName=!MenuName!,"%%~B"")
    if "!MenuLoca!" EQU "" (set "MenuLoca="%%~C"") else (set "MenuLoca=!MenuLoca!,"%%~C"")
  )
)

REM Display menu of choices
set /a i=0
for %%A in (%MenuName%) do (
  set /a i+=1
  echo.   [!i!] - %%~A
)

REM Prompt them for a selection
set /P "Choice=Please Select one of the above options (1-%i%): "
if "%Choice%" NEQ "" if %Choice% GEQ 1 if %Choice% LEQ %i% goto :ProcessChoice
echo Invalid choice slected.
exit /b

REM Process their menu choice
:ProcessChoice
for /F "tokens=%Choice% delims=," %%A in ("%MenuName%") do set Name=%%~A
for /F "tokens=%Choice% delims=," %%A in ("%MenuLoca%") do set Loca=%%~A
echo Choice: %Choice%
echo Name: %Name%
echo Loca: %Loca%
REM ... rest of script logic goes here ...
exit /b

Open in new window

a final note:

when you runt he commands you should put the quotes back on them manually.  it's always best to control the quotes yourself back and forth in my opinion instead of trying to store variables with quotes around them,, because variable manip becomes harder

  The obvious exclusion to this rule is lists such as Bill used where you want to ensure each term int he list has quotes around it followed by a comma in case of spaces.
Sorry on quotes around first element,  change:

    if "!MenuName!" EQU "" (set MenuName="%%~B") else (set MenuName=!MenuName!,%%~B)
    if "!MenuLoca!" EQU "" (set MenuLoca="%%~C") else (set MenuLoca=!MenuLoca!,%%~C)


to:

    if "!MenuName!" EQU "" (set MenuName=%%~B) else (set MenuName=!MenuName!,%%~B)
    if "!MenuLoca!" EQU "" (set MenuLoca=%%~C) else (set MenuLoca=!MenuLoca!,%%~C)


~bp
Avatar of Jofnn

ASKER

Thanks QC & Bill,

I've amended that and it works great!  Thanks again!

If you have no objections, I'm going to split the points at 250 each as both solutions that you have provided work perfectly for what I initially asked for and I will be able to add all my own code around this... As mentioned, I "PERSONALLY" prefer Bill's but this is all down to whoever finds this question in the future!

Is that ok?
Jonathon
Fine by me.  Glad to have been of some assistance.

~bp
Avatar of Jofnn

ASKER

Based on my initial question - BOTH of these alternatives work beatifully!

Thanks to both experts for their patience and assistance!
Hey totally cool by me, there is more than one way to skin a cat and I appreciate that you are looking out for the future members who come looking for solutions, thanks very much for the points =)